@vantageos/mcp-boilerplate 0.1.2 → 0.2.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/.claude-plugin/plugin.json +16 -8
- package/.mcp.json +8 -0
- package/CHANGELOG.md +55 -1
- package/README.fr.md +177 -142
- package/README.md +170 -133
- package/dist/auth.d.ts +20 -10
- package/dist/auth.js +9 -8
- package/dist/config.d.ts +10 -3
- package/dist/config.js +31 -3
- package/dist/exampleItems.d.ts +5 -4
- package/dist/exampleItems.js +3 -0
- package/dist/http.js +11 -1
- package/dist/httpApp.d.ts +23 -30
- package/dist/httpApp.js +73 -74
- package/dist/i18n.d.ts +6 -0
- package/dist/i18n.js +9 -0
- package/dist/locales/en.json +9 -0
- package/dist/locales/fr.json +9 -0
- package/dist/mcpServerFactory.d.ts +42 -41
- package/dist/mcpServerFactory.js +114 -165
- package/dist/oauth/authorizationServer.d.ts +56 -0
- package/dist/oauth/authorizationServer.js +423 -0
- package/dist/oauth/clerk.d.ts +15 -0
- package/dist/oauth/clerk.js +59 -0
- package/dist/oauth/clientRegistry.d.ts +50 -0
- package/dist/oauth/clientRegistry.js +203 -0
- package/dist/oauth/codeStore.d.ts +11 -0
- package/dist/oauth/codeStore.js +28 -0
- package/dist/oauth/picker.d.ts +11 -0
- package/dist/oauth/picker.js +67 -0
- package/dist/oauth/settings.d.ts +48 -0
- package/dist/oauth/settings.js +100 -0
- package/dist/oauth/tokenIssuer.d.ts +22 -0
- package/dist/oauth/tokenIssuer.js +61 -0
- package/dist/stdio.d.ts +7 -6
- package/dist/stdio.js +20 -19
- package/dist/tenancy.d.ts +16 -21
- package/dist/tenancy.js +12 -26
- package/dist/ui/uiResource.d.ts +36 -3
- package/dist/ui/uiResource.js +40 -6
- package/dist/version.d.ts +2 -0
- package/dist/version.js +8 -0
- package/docs/architecture.md +82 -0
- package/docs/oauth-stores.md +60 -0
- package/examples/oauth.env.example +25 -0
- package/mcp.json +19 -0
- package/package.json +22 -13
- package/plugin.json +43 -0
- package/scripts/plugin-manifest.source.json +35 -0
- package/skills/connect-mcp-server/SKILL.md +40 -0
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vantage-mcp-boilerplate",
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"author":
|
|
3
|
+
"displayName": "VantageOS MCP Boilerplate",
|
|
4
|
+
"version": "0.2.0",
|
|
5
|
+
"description": "Clonable MCP server template: SDK v2 on protocol 2026-07-28 (stdio + stateless Streamable HTTP), its own OAuth 2.1 authorization server (Clerk person + organisation), MCP Apps UI, FR/EN i18n and a declarative eval harness. Scaffold, extend, publish.",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "ElPi Corp / Laurent Perello"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/elpiarthera/vantage-mcp-boilerplate#readme",
|
|
10
|
+
"repository": "https://github.com/elpiarthera/vantage-mcp-boilerplate",
|
|
7
11
|
"license": "MIT",
|
|
8
|
-
"homepage": "https://github.com/elpiarthera/vantage-mcp-boilerplate",
|
|
9
|
-
"category": "mcp-server",
|
|
10
|
-
"bilingual": true,
|
|
11
12
|
"keywords": [
|
|
12
13
|
"mcp",
|
|
13
14
|
"claude-code",
|
|
15
|
+
"chatgpt",
|
|
14
16
|
"boilerplate",
|
|
15
17
|
"starter",
|
|
16
18
|
"template",
|
|
@@ -18,8 +20,14 @@
|
|
|
18
20
|
"streamable-http",
|
|
19
21
|
"oauth",
|
|
20
22
|
"multi-tenant",
|
|
23
|
+
"mcp-apps",
|
|
21
24
|
"i18n",
|
|
22
25
|
"vantage",
|
|
23
26
|
"elpi"
|
|
24
|
-
]
|
|
27
|
+
],
|
|
28
|
+
"metadata": {
|
|
29
|
+
"description_fr": "Template de serveur MCP clonable : SDK v2 sur le protocole 2026-07-28 (stdio + Streamable HTTP sans état), serveur d'autorisation OAuth 2.1 propre (personne + organisation Clerk), UI MCP Apps, i18n FR/EN et un harnais d'évaluations déclaratif. Scaffolder, étendre, publier.",
|
|
30
|
+
"category": "mcp-server",
|
|
31
|
+
"bilingual": true
|
|
32
|
+
}
|
|
25
33
|
}
|
package/.mcp.json
ADDED
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,60 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-10-10
|
|
11
|
+
|
|
12
|
+
SDK v2 / protocol 2026-07-28 migration, own authorization server, MCP Apps + ChatGPT
|
|
13
|
+
metadata, generated packaging manifests. Breaking for forks that import the removed SDK.
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
- **BREAKING**: `@modelcontextprotocol/sdk` replaced by `@modelcontextprotocol/server`
|
|
18
|
+
^2.3.1 (`@modelcontextprotocol/client` ^2.3.1 as a devDependency); Zod ^4.4.3 everywhere
|
|
19
|
+
(the `zod` / `zod/v4` mix is gone); `@modelcontextprotocol/ext-apps` ^2.0.3;
|
|
20
|
+
`@vantageos/cloud-identity` ^0.15.0, now the ONLY identity layer: its resource-server
|
|
21
|
+
entry point (`verifyMcpAccessToken`, `buildProtectedResourceMetadata`) replaces
|
|
22
|
+
`@vantageos/mcp-auth-kit`. `AuthConfig.jwks` is a `{ keys }` JWK set or a function returning
|
|
23
|
+
one (the external-issuer tier fetches and caches it); an unreachable key set answers 503,
|
|
24
|
+
never 401. `jose` remains for minting only.
|
|
25
|
+
- HTTP is `createMcpHandler(factory)` and stdio is `serveStdio(factory)`: one factory serves
|
|
26
|
+
the 2026-07-28 era (`server/discover`) and the 2025 era (`initialize`). The 2025 floor is
|
|
27
|
+
2025-06-18 (`supportedProtocolVersions`). `ttlMs` / `cacheScope` come from the SDK's
|
|
28
|
+
`cacheHints`. Legacy HTTP responses are now server-sent-event framed.
|
|
29
|
+
- The advertised server version is derived from `package.json` (was the literal `0.1.0`).
|
|
30
|
+
- `tenancy.ts` leaves the deprecated `scopeFilterList` for cloud-identity's `isRowInTenant`:
|
|
31
|
+
rows are selected by their stored `orgId` (added to `ExampleItem`), never by a creator name.
|
|
32
|
+
- `render_items_ui` now refuses an organisation-less caller on the OAuth tier, like
|
|
33
|
+
`list_items` (it used to fall through to the unscoped items).
|
|
34
|
+
- `.claude-plugin/plugin.json`: `description_fr`, `category`, `bilingual` moved under `metadata`.
|
|
35
|
+
- `docs/architecture.md`: wire posture rewritten; the false "no auth here" section removed.
|
|
36
|
+
|
|
37
|
+
### Added
|
|
38
|
+
|
|
39
|
+
- Own OAuth 2.1 authorization server (`src/oauth/`): `/authorize`, `/authorize/callback`,
|
|
40
|
+
`/authorize/org` (person + organisation picker, FR/EN), `/token` (PKCE S256), `/register`
|
|
41
|
+
(DCR fallback), CIMD client ids (SSRF-guarded), RFC 8414 metadata, `/.well-known/jwks.json`,
|
|
42
|
+
`/userinfo`, RFC 9207 `iss`, PRM at `/.well-known/oauth-protected-resource/mcp`. Decisions
|
|
43
|
+
delegated to cloud-identity; missing configuration answers 503. Code store and client registry
|
|
44
|
+
are interfaces with in-memory adapters (`docs/oauth-stores.md`).
|
|
45
|
+
- Tool records: `title`, the four annotations as explicit booleans, input AND output schemas,
|
|
46
|
+
`structuredContent` plus a same-JSON text block on all three tools.
|
|
47
|
+
`tests/toolRecordComplete.test.ts` fails on any gap.
|
|
48
|
+
- MCP Apps: resource `_meta.ui` (`csp` four domain arrays, `prefersBorder`, `domain` from
|
|
49
|
+
`MCP_UI_DOMAIN`), explicit tool `visibility`, additive `openai/outputTemplate` and
|
|
50
|
+
`openai/toolInvocation/*` keys, the resource-read locale taken from the caller.
|
|
51
|
+
- Packaging generated from one source (`scripts/plugin-manifest.source.json`): `.mcp.json`,
|
|
52
|
+
OpenAI `plugin.json` + `mcp.json`, onboarding skill `skills/connect-mcp-server/SKILL.md`,
|
|
53
|
+
`scripts/generate-signing-key.mjs`, `examples/oauth.env.example`.
|
|
54
|
+
|
|
55
|
+
### Removed
|
|
56
|
+
|
|
57
|
+
- `@vantageos/mcp-protocol-kit` dependency: the SDK emits `resultType` / cache fields itself.
|
|
58
|
+
- The private `_requestHandlers` access and the hand-rolled `server/discover` request schema.
|
|
59
|
+
|
|
60
|
+
### Known
|
|
61
|
+
|
|
62
|
+
- Per-organisation API keys for non-ChatGPT clients were never part of this code; not added.
|
|
63
|
+
|
|
10
64
|
## [0.1.2] - 2026-08-07
|
|
11
65
|
|
|
12
66
|
Two derive-never-type fixes, released together.
|
|
@@ -50,7 +104,7 @@ Initial published release.
|
|
|
50
104
|
`@modelcontextprotocol/sdk` transports: stdio (`StdioServerTransport`) and
|
|
51
105
|
stateless Streamable HTTP (`WebStandardStreamableHTTPServerTransport`).
|
|
52
106
|
- `@vantageos/mcp-protocol-kit` wiring for shared result types.
|
|
53
|
-
- OAuth 2.1 resource-server auth layer (
|
|
107
|
+
- OAuth 2.1 resource-server auth layer (since replaced, see 0.2.0), with a
|
|
54
108
|
public no-auth tier alongside the authenticated tier.
|
|
55
109
|
- Multi-tenant scoping layer with proven tenant isolation.
|
|
56
110
|
- MCP Apps UI extension: `ui://` resource serving a bundled widget
|
package/README.fr.md
CHANGED
|
@@ -1,95 +1,146 @@
|
|
|
1
1
|
# @vantageos/mcp-boilerplate
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
Streamable HTTP, sans état) construit exclusivement sur les transports
|
|
5
|
-
officiels du [`@modelcontextprotocol/sdk`](https://www.npmjs.com/package/@modelcontextprotocol/sdk).
|
|
6
|
-
Aucune route REST personnalisée ne se place jamais devant un appel d'outil.
|
|
3
|
+
VENDABLE SOUS : `mcp-boilerplate` — un starter de serveur MCP clonable pour la marketplace Claude Code / le catalogue VantageRegistry et pour ChatGPT, publié sous `@vantageos/mcp-boilerplate` sur npm.
|
|
7
4
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
5
|
+
Un squelette de serveur MCP clonable sur le SDK officiel
|
|
6
|
+
[`@modelcontextprotocol/server`](https://www.npmjs.com/package/@modelcontextprotocol/server)
|
|
7
|
+
v2 : protocole **2026-07-28** avec la poignée de main 2025 toujours servie, stdio
|
|
8
|
+
et Streamable HTTP sans état depuis une seule définition de serveur, son **propre
|
|
9
|
+
serveur d'autorisation OAuth 2.1** (une personne Clerk choisit une organisation),
|
|
10
|
+
une UI MCP Apps et des messages FR/EN. Aucune route REST personnalisée ne se place
|
|
11
|
+
jamais devant un appel d'outil.
|
|
12
|
+
|
|
13
|
+
Il embarque trois petits outils d'exemple (`ping`, `list_items`, `render_items_ui`)
|
|
14
|
+
qui exercent chaque couche. Remplacez-les par les vôtres ; le câblage reste.
|
|
13
15
|
|
|
14
16
|
## Structure
|
|
15
17
|
|
|
16
18
|
```
|
|
17
19
|
src/
|
|
18
20
|
mcpServerFactory.ts # définition unique du serveur MCP, partagée par les deux transports
|
|
19
|
-
|
|
21
|
+
version.ts # nom + version du serveur, dérivés de package.json
|
|
22
|
+
stdio.ts # serveStdio(factory) : les deux ères du protocole sur stdio
|
|
20
23
|
http.ts # adaptateur Node http.Server autour de l'app Hono
|
|
21
|
-
httpApp.ts #
|
|
22
|
-
auth.ts # câblage
|
|
23
|
-
config.ts #
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
24
|
+
httpApp.ts # createMcpHandler(factory) sur /mcp + la barrière OAuth
|
|
25
|
+
auth.ts # câblage resource-server (@vantageos/cloud-identity)
|
|
26
|
+
config.ts # AuthMode + AuthConfig d'émetteur externe, dérivés de l'environnement
|
|
27
|
+
tenancy.ts # JWT vérifié -> tenant ; lignes filtrées par orgId stocké
|
|
28
|
+
oauth/ # serveur d'autorisation propre (cloud-identity décide)
|
|
29
|
+
authorizationServer.ts settings.ts clerk.ts tokenIssuer.ts
|
|
30
|
+
clientRegistry.ts codeStore.ts picker.ts
|
|
31
|
+
ui/ # widget MCP Apps + ressource ui://
|
|
32
|
+
skills/connect-mcp-server/SKILL.md # skill d'onboarding (anglais)
|
|
33
|
+
scripts/
|
|
34
|
+
plugin-manifest.source.json # l'UNIQUE source de tous les manifestes de packaging
|
|
35
|
+
sync-plugin-version.mjs # génère les quatre manifestes (dans `npm run build`)
|
|
36
|
+
generate-signing-key.mjs # affiche une JWK privée RS256 pour MCP_OAUTH_SIGNING_JWK
|
|
37
|
+
tests/ evals/ docs/
|
|
33
38
|
```
|
|
34
39
|
|
|
35
40
|
## Démarrage rapide
|
|
36
41
|
|
|
37
42
|
```bash
|
|
38
43
|
npm install
|
|
39
|
-
npm run build
|
|
44
|
+
npm run build && npm run build:ui
|
|
40
45
|
|
|
41
46
|
# stdio (config client local, ex. Claude Code / Cursor) — jamais gardé,
|
|
42
47
|
# le process local qui invoque stdio EST l'appelant.
|
|
43
48
|
npm run start:stdio
|
|
44
49
|
|
|
45
|
-
# Streamable HTTP
|
|
46
|
-
MCP_OAUTH_ISSUER=https://your-tenant.example.com \
|
|
47
|
-
MCP_OAUTH_AUDIENCE=https://your-mcp-server.example.com \
|
|
48
|
-
PORT=8787 npm run start:http
|
|
49
|
-
|
|
50
|
-
# Streamable HTTP, palier "Public MCP" sans authentification (local/self-host, appelant de confiance) :
|
|
50
|
+
# Streamable HTTP, palier public (local/self-host, appelant de confiance) :
|
|
51
51
|
AUTH_MODE=public PORT=8787 npm run start:http
|
|
52
|
+
|
|
53
|
+
# Streamable HTTP, palier OAuth (défaut) : ce serveur est son propre serveur
|
|
54
|
+
# d'autorisation. Voir examples/oauth.env.example pour chaque variable requise.
|
|
55
|
+
node scripts/generate-signing-key.mjs # -> MCP_OAUTH_SIGNING_JWK
|
|
56
|
+
PORT=8787 npm run start:http
|
|
52
57
|
```
|
|
53
58
|
|
|
54
|
-
##
|
|
59
|
+
## Protocole et transports
|
|
55
60
|
|
|
56
|
-
|
|
61
|
+
Une seule fabrique (`createBoilerplateMcpServer`) construit un `McpServer` neuf par
|
|
62
|
+
requête (HTTP) ou par connexion (stdio). Le SDK tranche l'ère du protocole :
|
|
63
|
+
|
|
64
|
+
- un client **2026-07-28** ouvre par `server/discover` ; les résultats de
|
|
65
|
+
`tools/list`, `server/discover` et `resources/*` portent `resultType`, `ttlMs` et
|
|
66
|
+
`cacheScope` issus des `cacheHints` du SDK (définis une fois dans la fabrique) ;
|
|
67
|
+
- un client **ère 2025** ouvre par `initialize` et est servi par la branche legacy
|
|
68
|
+
sans état du SDK en HTTP, ou par une connexion épinglée en stdio.
|
|
69
|
+
|
|
70
|
+
Le plancher 2025 est **2025-06-18** : l'option `supportedProtocolVersions` du SDK ne
|
|
71
|
+
liste que `2025-11-25` et `2025-06-18` ; un `initialize` plus ancien se voit
|
|
72
|
+
proposer `2025-11-25`, jamais sa propre version. La version annoncée du serveur est
|
|
73
|
+
lue dans `package.json`.
|
|
74
|
+
|
|
75
|
+
## Fiches d'outils
|
|
76
|
+
|
|
77
|
+
Chaque outil déclare `title`, les quatre annotations en booléens explicites
|
|
78
|
+
(`readOnlyHint`, `destructiveHint`, `idempotentHint`, `openWorldHint`), un schéma
|
|
79
|
+
d'entrée et un schéma de sortie typés (Zod 4), et renvoie `structuredContent` plus
|
|
80
|
+
un bloc texte portant le même JSON. `tests/toolRecordComplete.test.ts` parcourt
|
|
81
|
+
`tools/list` et échoue au moindre manque. Les annotations sont des indications non
|
|
82
|
+
fiables pour le client (mcp-standard C-15) : ne conditionnez jamais une action sur
|
|
83
|
+
elles seules.
|
|
84
|
+
|
|
85
|
+
## Paliers d'authentification
|
|
57
86
|
|
|
58
87
|
| `AUTH_MODE` | Comportement |
|
|
59
88
|
|---|---|
|
|
60
|
-
| `oauth` (
|
|
61
|
-
| `public` |
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
`
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
`
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
| `oauth` (défaut) | OAuth 2.1. Sans `MCP_OAUTH_ISSUER`, ce serveur est à la fois serveur d'autorisation et serveur de ressources (ci-dessous). Avec `MCP_OAUTH_ISSUER` + `MCP_OAUTH_AUDIENCE`, il est serveur de ressources seul, pour un émetteur externe. Jeton absent ou invalide -> `401` + `WWW-Authenticate` pointant vers la PRM. |
|
|
90
|
+
| `public` | Aucune barrière en HTTP — local/self-host, appelant de confiance. Aucun document de serveur d'autorisation n'est servi. |
|
|
91
|
+
|
|
92
|
+
stdio n'est jamais gardé, dans aucun des deux modes.
|
|
93
|
+
|
|
94
|
+
### Serveur d'autorisation propre (arbitrage R1)
|
|
95
|
+
|
|
96
|
+
| Route | Rôle |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `GET /.well-known/oauth-authorization-server` (+ `openid-configuration`) | métadonnées RFC 8414 : S256 seul, `iss` RFC 9207, CIMD et DCR annoncés |
|
|
99
|
+
| `GET /.well-known/oauth-protected-resource/mcp` | PRM RFC 9728 (`resource` = `<base>/mcp`) via cloud-identity |
|
|
100
|
+
| `GET /.well-known/jwks.json` | clés de signature publiques uniquement |
|
|
101
|
+
| `POST /register` | repli Dynamic Client Registration (clients publics, PKCE) |
|
|
102
|
+
| `GET /authorize`, `GET /authorize/callback`, `POST /authorize/org` | connexion Clerk, puis sélecteur personne + organisation avec consentement |
|
|
103
|
+
| `POST /token` | `authorization_code` + PKCE S256 -> JWT RS256 lié à la ressource, à l'émetteur et à l'organisation choisie |
|
|
104
|
+
| `GET /userinfo` | sujet OIDC (et e-mail quand le scope le permet) |
|
|
105
|
+
|
|
106
|
+
Toute décision — contrôles redirect/PKCE/ressource/scope, jeton de consentement,
|
|
107
|
+
liaison et échange du code, formes de la découverte et de userinfo — est prise par
|
|
108
|
+
`@vantageos/cloud-identity` (`startAuthorize`, `resumeAuthorize`,
|
|
109
|
+
`exchangeAuthorizationCode`, `buildDiscoveryDocument`, `buildUserInfo`). Cette app
|
|
110
|
+
fournit l'environnement, les stores, les appels Clerk, la page de sélection et la
|
|
111
|
+
signature. Un client peut s'identifier par l'URL d'un Client ID Metadata Document
|
|
112
|
+
(récupéré avec garde SSRF) ou s'enregistrer via `/register`.
|
|
113
|
+
|
|
114
|
+
**Fail-closed** : une variable manquante (`PUBLIC_BASE_URL`, `CLERK_DOMAIN`,
|
|
115
|
+
`AUTHORIZE_SIGN_IN_URL`, `AUTHORIZE_CALLBACK_URL`, `AUTHORIZE_STATE_SECRET`,
|
|
116
|
+
`CLERK_SECRET_KEY`, `MCP_OAUTH_SIGNING_JWK`) répond `503` et est nommée dans le
|
|
117
|
+
journal (les noms, jamais les valeurs). Il n'existe aucun chemin d'approbation
|
|
118
|
+
automatique.
|
|
119
|
+
|
|
120
|
+
**Stores** : le store des codes d'autorisation et le registre des clients sont des
|
|
121
|
+
interfaces avec adaptateurs en mémoire pour les tests et le développement
|
|
122
|
+
mono-processus. Utilisez des adaptateurs durables en production — contrat dans
|
|
123
|
+
[`docs/oauth-stores.md`](docs/oauth-stores.md).
|
|
124
|
+
|
|
125
|
+
**Non inclus** : jetons de rafraîchissement (les jetons d'accès durent une heure),
|
|
126
|
+
clés d'API par organisation pour les clients hors ChatGPT (ce code n'a jamais eu ce
|
|
127
|
+
chemin ; c'est un manque, pas une régression), URI de redirection à joker, et liste
|
|
128
|
+
de révocation des jetons.
|
|
129
|
+
|
|
130
|
+
### Multi-tenance
|
|
131
|
+
|
|
132
|
+
`list_items` filtre les lignes par leur `orgId` stocké face à l'organisation
|
|
133
|
+
vérifiée de l'appelant (`isRowInTenant`) ; un jeton sans organisation reçoit un
|
|
134
|
+
refus d'outil explicite, jamais une vue non filtrée. Ne résolvez le tenant de
|
|
135
|
+
l'appelant qu'à partir du jeton vérifié, jamais d'un argument d'outil.
|
|
136
|
+
|
|
137
|
+
## i18n côté serveur (FR/EN)
|
|
138
|
+
|
|
139
|
+
`src/i18n.ts` localise les messages d'erreur/validation MCP et la page de sélection :
|
|
140
|
+
le client envoie `X-MCP-Locale: fr|en` (défaut `en`) ; le sélecteur navigateur suit
|
|
141
|
+
`Accept-Language`. Les fichiers de ressources (`src/locales/{en,fr}.json`) gardent la
|
|
142
|
+
convention `<Namespace>.<category>.<key>` de `@vantageos/mosaic-i18n`, FR et EN en
|
|
143
|
+
parité.
|
|
93
144
|
|
|
94
145
|
## Tests
|
|
95
146
|
|
|
@@ -97,97 +148,81 @@ de son fichier de ressources est réutilisée à la place, sous le namespace
|
|
|
97
148
|
npm test
|
|
98
149
|
```
|
|
99
150
|
|
|
100
|
-
Exécute les deux transports
|
|
101
|
-
|
|
102
|
-
`dist/stdio.js`
|
|
151
|
+
Exécute les deux transports via les vrais chemins de code du SDK : la suite HTTP
|
|
152
|
+
pilote l'app Hono en processus avec le `Client` du SDK v2 ; la suite stdio lance le
|
|
153
|
+
`dist/stdio.js` construit. `npm run evals` exécute le jeu de cas déclaratif contre
|
|
154
|
+
stdio.
|
|
103
155
|
|
|
104
156
|
## Ajouter un outil
|
|
105
157
|
|
|
106
|
-
Enregistrez-le une
|
|
107
|
-
|
|
108
|
-
|
|
158
|
+
Enregistrez-le une fois dans `src/mcpServerFactory.ts` avec `title`, les quatre
|
|
159
|
+
annotations, `inputSchema` et `outputSchema`, et renvoyez `structuredResult(...)`. Il
|
|
160
|
+
est alors servi sur les deux transports et les deux ères du protocole.
|
|
161
|
+
`tests/toolRecordComplete.test.ts` le garde honnête.
|
|
109
162
|
|
|
110
163
|
## Packaging & distribution
|
|
111
164
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
165
|
+
Quatre manifestes sont **générés** depuis `scripts/plugin-manifest.source.json` et
|
|
166
|
+
`package.json` par `scripts/sync-plugin-version.mjs` (dans `npm run build`). Ne les
|
|
167
|
+
éditez jamais à la main ; `tests/pluginManifests.test.ts` échoue sur toute dérive.
|
|
168
|
+
|
|
169
|
+
| Fichier | Pour |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `.claude-plugin/plugin.json` | marketplace Claude Code ; les champs non standard (`description_fr`, `category`, `bilingual`) sont sous `metadata` |
|
|
172
|
+
| `.mcp.json` | Claude Code : `type: http`, URL seule, aucun secret |
|
|
173
|
+
| `plugin.json` + `mcp.json` | OpenAI Agent Plugins ; `streamable-http`, auth sous `extensions["com.openai"].auth` (OAuth, CIMD préféré, repli DCR) |
|
|
174
|
+
|
|
175
|
+
L'URL du serveur est un placeholder unique dans le fichier source. Fixez-la pour un
|
|
176
|
+
vrai déploiement avec `MCP_SERVER_URL=https://votre-hote/mcp node scripts/sync-plugin-version.mjs`
|
|
177
|
+
(les deux entrées changent), ou laissez `${MCP_BOILERPLATE_URL:-<placeholder>}` de
|
|
178
|
+
Claude être surchargé par l'installateur. Le skill d'onboarding est
|
|
179
|
+
`skills/connect-mcp-server/SKILL.md`, déclaré pour OpenAI en
|
|
180
|
+
`extensions["com.openai"].onboardingSkill`.
|
|
128
181
|
|
|
129
182
|
### Scaffold → build → publish
|
|
130
183
|
|
|
131
184
|
```bash
|
|
132
|
-
# 1. Scaffold : clonez ce repo (ou le tarball npm) comme squelette de votre serveur.
|
|
133
185
|
git clone https://github.com/elpiarthera/vantage-mcp-boilerplate.git my-mcp
|
|
134
|
-
cd my-mcp && npm install
|
|
135
|
-
|
|
136
|
-
#
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
#
|
|
140
|
-
rm -rf dist node_modules/.vite && npm run build && npm test && npm run evals
|
|
141
|
-
|
|
142
|
-
# 4. Inspectez le tarball avant publication — confirmez que dist/ est bien livré et que rien ne fuit :
|
|
143
|
-
npm pack --dry-run
|
|
144
|
-
|
|
145
|
-
# 5. Publiez sur les deux canaux :
|
|
146
|
-
npm publish # registre npm (prepublishOnly relance build+test+evals)
|
|
147
|
-
# Catalogue VantageRegistry : enregistrez .claude-plugin/plugin.json via
|
|
148
|
-
# le skill fleet create-plugin / generate-marketplace (upsert VR).
|
|
186
|
+
cd my-mcp && npm install
|
|
187
|
+
# Renommez "name"/"bin" dans package.json et les noms dans scripts/plugin-manifest.source.json,
|
|
188
|
+
# puis ajoutez vos outils dans src/mcpServerFactory.ts.
|
|
189
|
+
rm -rf dist && npm run build && npm run build:ui && npm test && npm run evals
|
|
190
|
+
npm pack --dry-run # vérifiez que dist/, les manifestes et skills/ sont livrés
|
|
191
|
+
npm publish # prepublishOnly relance build + test + evals
|
|
149
192
|
```
|
|
150
193
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
Les canaux de distribution sont **npm** (le package exécutable) et le
|
|
154
|
-
**catalogue VantageRegistry / Claude Code marketplace** (découverte, via
|
|
155
|
-
`plugin.json`).
|
|
194
|
+
La publication se prouve par le `dist-tag` du registre, jamais par le code de sortie
|
|
195
|
+
de `npm publish`.
|
|
156
196
|
|
|
157
197
|
## Couche UI MCP Apps
|
|
158
198
|
|
|
159
|
-
`render_items_ui`
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
**
|
|
185
|
-
|
|
186
|
-
`
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
besoin. Par conséquent, **le kit `@vantageos/mcp-protocol-kit` 0.2.0 DOIT
|
|
190
|
-
être publié sur npm AVANT que ce boilerplate puisse être installé ou publié
|
|
191
|
-
depuis le registre.** C'est un ordre strict, non optionnel : publiez d'abord
|
|
192
|
-
le kit, confirmez que 0.2.0 est en ligne sur le registre, puis publiez le
|
|
193
|
-
boilerplate.
|
|
199
|
+
`render_items_ui` rend `EXAMPLE_ITEMS` sous forme de widget interactif
|
|
200
|
+
(`io.modelcontextprotocol/ui`, spec 2026-01-26).
|
|
201
|
+
|
|
202
|
+
- L'outil relie sa ressource `ui://` par la clé imbriquée `_meta.ui.resourceUri`
|
|
203
|
+
avec `visibility: ["model", "app"]` explicite. L'outil est enregistré avec
|
|
204
|
+
`server.registerTool`, et non `registerAppTool` d'ext-apps, car ce helper écrit
|
|
205
|
+
aussi la clé plate dépréciée `_meta["ui/resourceUri"]` (mcp-app-standard A-6).
|
|
206
|
+
- La ressource déclare `_meta.ui` : `csp` avec `connectDomains`, `resourceDomains`,
|
|
207
|
+
`frameDomains` et `baseUriDomains` (tous vides — le widget ne fait aucune requête
|
|
208
|
+
réseau, ne charge aucun actif tiers et n'intègre aucune frame), `prefersBorder`, et
|
|
209
|
+
`domain` seulement quand `MCP_UI_DOMAIN` est défini (le format dépend de l'hôte).
|
|
210
|
+
Son type MIME est `text/html;profile=mcp-app`.
|
|
211
|
+
- **Les clés ChatGPT sont additives** : `openai/outputTemplate` (même URI) et les
|
|
212
|
+
chaînes `openai/toolInvocation/invoking|invoked`. Rien ne les lit ; le serveur
|
|
213
|
+
fonctionne sans aucune clé `openai/*`. Les entrées `openai/ui` ne sont pas
|
|
214
|
+
émises — elles sont optionnelles ; ajoutez-les au `_meta` de l'outil dans
|
|
215
|
+
`src/ui/uiResource.ts` si votre app ChatGPT en a besoin.
|
|
216
|
+
- Un repli markdown est toujours présent dans le résultat d'appel, pour les clients
|
|
217
|
+
qui ne négocient jamais l'extension.
|
|
218
|
+
|
|
219
|
+
Le widget (`src/ui/`) réutilise une famille `MosaicCard` vendorisée et est empaqueté
|
|
220
|
+
en un seul fichier HTML autonome (`vite-plugin-singlefile`,
|
|
221
|
+
`dist/ui-bundle/widget.html`) par `npm run build:ui`, sur un chemin qui n'entre
|
|
222
|
+
jamais en collision avec `dist/ui/` de tsc.
|
|
223
|
+
|
|
224
|
+
**Couche d'identité** : `@vantageos/cloud-identity` (^0.15.0) est le seul paquet
|
|
225
|
+
d'identité. Son point d'entrée resource-server (`verifyMcpAccessToken`,
|
|
226
|
+
`buildProtectedResourceMetadata`) vérifie le jeton Bearer sur `/mcp` ; `jose` ne sert
|
|
227
|
+
plus qu'à signer les jetons d'accès du serveur d'autorisation. Un jeu de clés
|
|
228
|
+
injoignable répond 503, jamais 401.
|