@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
package/dist/httpApp.d.ts
CHANGED
|
@@ -1,47 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Hono HTTP app:
|
|
2
|
+
* Hono HTTP app: the MCP endpoint plus, on the OAuth tier, this server's own authorization
|
|
3
|
+
* server (ruling R1).
|
|
3
4
|
*
|
|
4
|
-
* The MCP wire protocol
|
|
5
|
-
*
|
|
6
|
-
* (stateless
|
|
7
|
-
*
|
|
8
|
-
* as it would over any other MCP HTTP server. No ad-hoc REST routes stand
|
|
9
|
-
* in front of tool calls.
|
|
5
|
+
* The MCP wire protocol is served by the SDK v2 `createMcpHandler` — ONE handler serves both
|
|
6
|
+
* protocol eras on `/mcp`: a 2026-07-28 client opens with `server/discover`, a 2025-era client
|
|
7
|
+
* with `initialize` (served through the SDK's stateless legacy leg). No ad-hoc REST routes
|
|
8
|
+
* stand in front of tool calls.
|
|
10
9
|
*
|
|
11
|
-
* True statelessness: a FRESH `McpServer`
|
|
12
|
-
*
|
|
13
|
-
* one request, and never retained. Nothing survives past the request/
|
|
14
|
-
* response cycle, so the endpoint is horizontally load-balanceable and two
|
|
15
|
-
* concurrent requests never share any in-memory object.
|
|
16
|
-
*
|
|
17
|
-
* `server/discover` is NOT a separate REST route: it is a JSON-RPC method
|
|
18
|
-
* on this same `/mcp` endpoint (registered on the low-level `Server` in
|
|
19
|
-
* mcpServerFactory.ts via `@vantageos/mcp-protocol-kit`'s
|
|
20
|
-
* `handleServerDiscover`), exactly like `tools/list` and `tools/call` —
|
|
21
|
-
* a standard MCP client dispatches all three the same way.
|
|
10
|
+
* True statelessness: a FRESH `McpServer` is built for every request by the handler's factory
|
|
11
|
+
* and never retained, so the endpoint is horizontally load-balanceable.
|
|
22
12
|
*
|
|
23
13
|
* Auth tiers (mcp-standard.md "Authentication & Authorization"):
|
|
24
14
|
*
|
|
25
|
-
* - `authMode: "oauth"` —
|
|
26
|
-
*
|
|
27
|
-
* (
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* when `buildHttpApp()` is called with no deps, so the transport-only
|
|
35
|
-
* tests (httpHandshake.test.ts, protocolKitWiring.test.ts) exercise the
|
|
36
|
-
* skeleton without needing OAuth configuration.
|
|
15
|
+
* - `authMode: "oauth"` with `authorizationServer` — the default: this app is BOTH the OAuth
|
|
16
|
+
* 2.1 authorization server (src/oauth/, every decision delegated to @vantageos/cloud-identity)
|
|
17
|
+
* and the resource server (@vantageos/cloud-identity resource-server). Every /mcp call needs a valid Bearer
|
|
18
|
+
* JWT bound to `<base>/mcp` (RFC 8707) and to this issuer. Missing configuration answers
|
|
19
|
+
* 503 (fail-closed), never an open endpoint.
|
|
20
|
+
* - `authMode: "oauth"` with `auth` — resource server only, for a deployment that keeps an
|
|
21
|
+
* EXTERNAL issuer (config.ts `loadAuthConfig`).
|
|
22
|
+
* - `authMode: "public"` — no gate at all, for local/self-host use where the caller is already
|
|
23
|
+
* trusted (mirrors stdio's trust model). The DEFAULT when `buildHttpApp()` gets no deps.
|
|
37
24
|
*/
|
|
38
25
|
import { Hono } from "hono";
|
|
39
26
|
import { type AuthConfig } from "./auth.js";
|
|
40
27
|
import type { AuthMode } from "./config.js";
|
|
28
|
+
import { type AuthorizationServerDeps } from "./oauth/authorizationServer.js";
|
|
41
29
|
import { type ResolvedTenant } from "./tenancy.js";
|
|
42
30
|
export interface AppDeps {
|
|
43
31
|
authMode?: AuthMode;
|
|
32
|
+
/** External-issuer resource-server configuration (no authorization server mounted). */
|
|
44
33
|
auth?: AuthConfig;
|
|
34
|
+
/** Own authorization server (R1). Takes precedence over `auth`. */
|
|
35
|
+
authorizationServer?: AuthorizationServerDeps;
|
|
36
|
+
/** Dedicated sandbox origin for the MCP Apps widget (`_meta.ui.domain`). */
|
|
37
|
+
uiDomain?: string;
|
|
45
38
|
}
|
|
46
39
|
type Variables = {
|
|
47
40
|
tenant?: ResolvedTenant;
|
package/dist/httpApp.js
CHANGED
|
@@ -1,113 +1,112 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Hono HTTP app:
|
|
2
|
+
* Hono HTTP app: the MCP endpoint plus, on the OAuth tier, this server's own authorization
|
|
3
|
+
* server (ruling R1).
|
|
3
4
|
*
|
|
4
|
-
* The MCP wire protocol
|
|
5
|
-
*
|
|
6
|
-
* (stateless
|
|
7
|
-
*
|
|
8
|
-
* as it would over any other MCP HTTP server. No ad-hoc REST routes stand
|
|
9
|
-
* in front of tool calls.
|
|
5
|
+
* The MCP wire protocol is served by the SDK v2 `createMcpHandler` — ONE handler serves both
|
|
6
|
+
* protocol eras on `/mcp`: a 2026-07-28 client opens with `server/discover`, a 2025-era client
|
|
7
|
+
* with `initialize` (served through the SDK's stateless legacy leg). No ad-hoc REST routes
|
|
8
|
+
* stand in front of tool calls.
|
|
10
9
|
*
|
|
11
|
-
* True statelessness: a FRESH `McpServer`
|
|
12
|
-
*
|
|
13
|
-
* one request, and never retained. Nothing survives past the request/
|
|
14
|
-
* response cycle, so the endpoint is horizontally load-balanceable and two
|
|
15
|
-
* concurrent requests never share any in-memory object.
|
|
16
|
-
*
|
|
17
|
-
* `server/discover` is NOT a separate REST route: it is a JSON-RPC method
|
|
18
|
-
* on this same `/mcp` endpoint (registered on the low-level `Server` in
|
|
19
|
-
* mcpServerFactory.ts via `@vantageos/mcp-protocol-kit`'s
|
|
20
|
-
* `handleServerDiscover`), exactly like `tools/list` and `tools/call` —
|
|
21
|
-
* a standard MCP client dispatches all three the same way.
|
|
10
|
+
* True statelessness: a FRESH `McpServer` is built for every request by the handler's factory
|
|
11
|
+
* and never retained, so the endpoint is horizontally load-balanceable.
|
|
22
12
|
*
|
|
23
13
|
* Auth tiers (mcp-standard.md "Authentication & Authorization"):
|
|
24
14
|
*
|
|
25
|
-
* - `authMode: "oauth"` —
|
|
26
|
-
*
|
|
27
|
-
* (
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* when `buildHttpApp()` is called with no deps, so the transport-only
|
|
35
|
-
* tests (httpHandshake.test.ts, protocolKitWiring.test.ts) exercise the
|
|
36
|
-
* skeleton without needing OAuth configuration.
|
|
15
|
+
* - `authMode: "oauth"` with `authorizationServer` — the default: this app is BOTH the OAuth
|
|
16
|
+
* 2.1 authorization server (src/oauth/, every decision delegated to @vantageos/cloud-identity)
|
|
17
|
+
* and the resource server (@vantageos/cloud-identity resource-server). Every /mcp call needs a valid Bearer
|
|
18
|
+
* JWT bound to `<base>/mcp` (RFC 8707) and to this issuer. Missing configuration answers
|
|
19
|
+
* 503 (fail-closed), never an open endpoint.
|
|
20
|
+
* - `authMode: "oauth"` with `auth` — resource server only, for a deployment that keeps an
|
|
21
|
+
* EXTERNAL issuer (config.ts `loadAuthConfig`).
|
|
22
|
+
* - `authMode: "public"` — no gate at all, for local/self-host use where the caller is already
|
|
23
|
+
* trusted (mirrors stdio's trust model). The DEFAULT when `buildHttpApp()` gets no deps.
|
|
37
24
|
*/
|
|
38
|
-
import {
|
|
25
|
+
import { createMcpHandler } from "@modelcontextprotocol/server";
|
|
39
26
|
import { Hono } from "hono";
|
|
40
27
|
import { authenticateRequest, protectedResourceMetadataDocument } from "./auth.js";
|
|
41
28
|
import { detectLocale, t } from "./i18n.js";
|
|
42
29
|
import { createBoilerplateMcpServer } from "./mcpServerFactory.js";
|
|
30
|
+
import { createAuthorizationServer, } from "./oauth/authorizationServer.js";
|
|
43
31
|
import { resolveTenantFromJwtPayload } from "./tenancy.js";
|
|
44
|
-
const
|
|
32
|
+
const PRM_PATHS = [
|
|
33
|
+
"/.well-known/oauth-protected-resource",
|
|
34
|
+
"/.well-known/oauth-protected-resource/mcp",
|
|
35
|
+
];
|
|
45
36
|
export function buildHttpApp(deps = {}) {
|
|
46
37
|
const app = new Hono();
|
|
47
38
|
const authMode = deps.authMode ?? "public";
|
|
48
39
|
app.get("/health", (c) => c.json({ status: "ok" }));
|
|
49
40
|
if (authMode === "oauth") {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
41
|
+
// Resolves the resource-server configuration per request: the own-AS tier reads its
|
|
42
|
+
// environment lazily, so a missing value is a 503 on the request, not a crash at boot.
|
|
43
|
+
let resolve;
|
|
44
|
+
if (deps.authorizationServer) {
|
|
45
|
+
const as = createAuthorizationServer(deps.authorizationServer);
|
|
46
|
+
as.mount(app);
|
|
47
|
+
resolve = () => as.resourceServer();
|
|
53
48
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
49
|
+
else if (deps.auth) {
|
|
50
|
+
const auth = deps.auth;
|
|
51
|
+
resolve = async () => ({ ok: true, auth });
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
throw new Error("authMode 'oauth' requires an AuthConfig (deps.auth, external issuer) or an " +
|
|
55
|
+
"authorizationServer (own issuer, the default) — see config.ts and src/http.ts, or pass " +
|
|
56
|
+
"authMode: 'public' for the no-auth tier.");
|
|
57
|
+
}
|
|
58
|
+
for (const path of PRM_PATHS) {
|
|
59
|
+
app.get(path, async (c) => {
|
|
60
|
+
const rs = await resolve();
|
|
61
|
+
if (!rs.ok)
|
|
62
|
+
return c.json({ error: "temporarily_unavailable" }, 503);
|
|
63
|
+
return c.json(protectedResourceMetadataDocument(rs.auth));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
// OAuth 2.1 resource-server gate — /mcp requires a valid Bearer JWT, verified entirely by
|
|
67
|
+
// @vantageos/cloud-identity (`verifyMcpAccessToken`).
|
|
68
|
+
app.use("/mcp", async (c, next) => {
|
|
69
|
+
const rs = await resolve();
|
|
70
|
+
if (!rs.ok) {
|
|
71
|
+
console.error(`[oauth] /mcp refused: missing configuration ${rs.missing.join(", ")}`);
|
|
72
|
+
return c.json({ error: "temporarily_unavailable" }, 503);
|
|
64
73
|
}
|
|
65
|
-
const
|
|
66
|
-
const result = await authenticateRequest(authHeader, auth);
|
|
74
|
+
const result = await authenticateRequest(c.req.header("Authorization"), rs.auth);
|
|
67
75
|
if (!result.ok) {
|
|
76
|
+
if (result.status === 503) {
|
|
77
|
+
// Key set unreachable: an outage, never a bad credential — no challenge, no 401.
|
|
78
|
+
console.error("[oauth] /mcp refused: signing keys unavailable");
|
|
79
|
+
return c.json({ error: "temporarily_unavailable" }, 503);
|
|
80
|
+
}
|
|
68
81
|
const locale = detectLocale(c.req.header("X-MCP-Locale"));
|
|
69
82
|
c.header("WWW-Authenticate", result.headers["WWW-Authenticate"]);
|
|
70
83
|
return c.json({ error: t(locale, "auth.unauthorized") }, result.status);
|
|
71
84
|
}
|
|
72
|
-
// Tenant resolution (tenancy.ts) is attempted here but is NOT
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
// mcpServerFactory.ts) — a missing tenant there is refused as an MCP
|
|
78
|
-
// tool error, never silently treated as an unscoped/master caller.
|
|
85
|
+
// Tenant resolution (tenancy.ts) is attempted here but is NOT request-fatal:
|
|
86
|
+
// `initialize`/`tools/list` and any tenant-agnostic tool (e.g. `ping`) must keep working
|
|
87
|
+
// for a verified caller with no organization on their token. Tenant-scoped tools enforce
|
|
88
|
+
// the requirement themselves, at the tool-call level (see mcpServerFactory.ts) — a
|
|
89
|
+
// missing tenant there is refused as an MCP tool error, never treated as unscoped.
|
|
79
90
|
try {
|
|
80
|
-
c.set("tenant", resolveTenantFromJwtPayload(result.
|
|
91
|
+
c.set("tenant", resolveTenantFromJwtPayload(result.session.claims));
|
|
81
92
|
}
|
|
82
93
|
catch {
|
|
83
|
-
// No organization on this token — tenant stays unset.
|
|
84
|
-
// tools refuse explicitly when they see no tenant under oauth mode.
|
|
94
|
+
// No organization on this token — tenant stays unset.
|
|
85
95
|
}
|
|
86
96
|
await next();
|
|
87
97
|
});
|
|
88
98
|
}
|
|
89
|
-
// The one real MCP endpoint
|
|
90
|
-
|
|
91
|
-
// which handles the protocol's method dispatch itself.
|
|
92
|
-
app.all("/mcp", async (c) => {
|
|
99
|
+
// The one real MCP endpoint, both protocol eras (see header comment).
|
|
100
|
+
app.all("/mcp", (c) => {
|
|
93
101
|
const tenant = authMode === "oauth" ? c.get("tenant") : undefined;
|
|
94
102
|
const locale = detectLocale(c.req.header("X-MCP-Locale"));
|
|
95
|
-
const
|
|
103
|
+
const handler = createMcpHandler(() => createBoilerplateMcpServer({
|
|
96
104
|
tenant,
|
|
97
105
|
requireTenant: authMode === "oauth",
|
|
98
106
|
locale,
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
enableJsonResponse: true,
|
|
103
|
-
});
|
|
104
|
-
await server.connect(transport);
|
|
105
|
-
try {
|
|
106
|
-
return await transport.handleRequest(c.req.raw);
|
|
107
|
-
}
|
|
108
|
-
finally {
|
|
109
|
-
await transport.close();
|
|
110
|
-
}
|
|
107
|
+
uiDomain: deps.uiDomain,
|
|
108
|
+
}));
|
|
109
|
+
return handler.fetch(c.req.raw);
|
|
111
110
|
});
|
|
112
111
|
return app;
|
|
113
112
|
}
|
package/dist/i18n.d.ts
CHANGED
|
@@ -24,6 +24,12 @@ export type McpLocale = "en" | "fr";
|
|
|
24
24
|
* Defaults to "en" for a missing header or any value other than "fr".
|
|
25
25
|
*/
|
|
26
26
|
export declare function detectLocale(headerValue: string | null | undefined): McpLocale;
|
|
27
|
+
/**
|
|
28
|
+
* Resolves the locale of a browser navigation (the OAuth picker page) from `Accept-Language`,
|
|
29
|
+
* where no `X-MCP-Locale` header exists. The first listed language decides; "fr" selects
|
|
30
|
+
* French, anything else English.
|
|
31
|
+
*/
|
|
32
|
+
export declare function detectLocaleFromAcceptLanguage(headerValue: string | null | undefined): McpLocale;
|
|
27
33
|
/**
|
|
28
34
|
* Looks up a translated MCP message. `key` is a dot path under the `Mcp`
|
|
29
35
|
* namespace, e.g. `"auth.unauthorized"` or `"tenant.permissionDenied"`.
|
package/dist/i18n.js
CHANGED
|
@@ -43,6 +43,15 @@ const RESOURCES = {
|
|
|
43
43
|
export function detectLocale(headerValue) {
|
|
44
44
|
return headerValue?.toLowerCase() === "fr" ? "fr" : "en";
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Resolves the locale of a browser navigation (the OAuth picker page) from `Accept-Language`,
|
|
48
|
+
* where no `X-MCP-Locale` header exists. The first listed language decides; "fr" selects
|
|
49
|
+
* French, anything else English.
|
|
50
|
+
*/
|
|
51
|
+
export function detectLocaleFromAcceptLanguage(headerValue) {
|
|
52
|
+
const first = headerValue?.split(",")[0]?.trim().toLowerCase() ?? "";
|
|
53
|
+
return first === "fr" || first.startsWith("fr-") || first.startsWith("fr;") ? "fr" : "en";
|
|
54
|
+
}
|
|
46
55
|
/**
|
|
47
56
|
* Looks up a translated MCP message. `key` is a dot path under the `Mcp`
|
|
48
57
|
* namespace, e.g. `"auth.unauthorized"` or `"tenant.permissionDenied"`.
|
package/dist/locales/en.json
CHANGED
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
},
|
|
9
9
|
"validation": {
|
|
10
10
|
"messageTooLong": "message exceeds the maximum length of 200 characters"
|
|
11
|
+
},
|
|
12
|
+
"oauth": {
|
|
13
|
+
"pickerTitle": "Connect {client}",
|
|
14
|
+
"pickerIntro": "{client} asks to act on your behalf. Choose the organisation it may reach, then approve or deny.",
|
|
15
|
+
"pickerOrganisation": "Organisation",
|
|
16
|
+
"approve": "Approve",
|
|
17
|
+
"deny": "Deny",
|
|
18
|
+
"denied": "The request was denied; you can close this window.",
|
|
19
|
+
"unavailable": "Authorization is not configured on this server."
|
|
11
20
|
}
|
|
12
21
|
}
|
|
13
22
|
}
|
package/dist/locales/fr.json
CHANGED
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
},
|
|
9
9
|
"validation": {
|
|
10
10
|
"messageTooLong": "le message dépasse la longueur maximale de 200 caractères"
|
|
11
|
+
},
|
|
12
|
+
"oauth": {
|
|
13
|
+
"pickerTitle": "Connecter {client}",
|
|
14
|
+
"pickerIntro": "{client} demande à agir en votre nom. Choisissez l'organisation à laquelle il peut accéder, puis approuvez ou refusez.",
|
|
15
|
+
"pickerOrganisation": "Organisation",
|
|
16
|
+
"approve": "Approuver",
|
|
17
|
+
"deny": "Refuser",
|
|
18
|
+
"denied": "La demande a été refusée ; vous pouvez fermer cette fenêtre.",
|
|
19
|
+
"unavailable": "L'autorisation n'est pas configurée sur ce serveur."
|
|
11
20
|
}
|
|
12
21
|
}
|
|
13
22
|
}
|
|
@@ -1,60 +1,61 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Single MCP server definition shared by both transports
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* survives past the single request/response cycle.
|
|
2
|
+
* Single MCP server definition shared by both transports. It is registered once, on the SDK
|
|
3
|
+
* v2 `McpServer`, and served by `createMcpHandler` (HTTP, src/httpApp.ts) and `serveStdio`
|
|
4
|
+
* (src/stdio.ts) — each owns the protocol-era decision, so ONE factory serves a 2026-07-28
|
|
5
|
+
* client (opens with `server/discover`) and a 2025-era client (opens with `initialize`).
|
|
6
|
+
* Each call builds a fresh instance with no shared mutable state; the HTTP transport builds
|
|
7
|
+
* one per request, which is what keeps the endpoint load-balanceable.
|
|
9
8
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Wire posture:
|
|
10
|
+
* - the 2025-era floor is 2025-06-18: `supportedProtocolVersions` lists only 2025-11-25 and
|
|
11
|
+
* 2025-06-18 (the SDK option on `ProtocolOptions`); older revisions are never counter-offered;
|
|
12
|
+
* - `ttlMs` / `cacheScope` on 2026-07-28 results come from the SDK's `cacheHints` — no
|
|
13
|
+
* handler override, no private-field access, no hand-written discover schema;
|
|
14
|
+
* - the advertised version is `SERVER_VERSION`, derived from package.json.
|
|
13
15
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* kit emits that key directly now — no hand alias.
|
|
16
|
+
* Tool records (mcp-standard C-4, C-15): every tool declares `title`, the four behavioural
|
|
17
|
+
* annotations as explicit booleans, a typed input schema AND a typed output schema (Zod 4),
|
|
18
|
+
* and returns `structuredContent` plus a text block carrying the same JSON.
|
|
19
|
+
* tests/toolRecordComplete.test.ts walks `tools/list` and fails on any gap.
|
|
19
20
|
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* without breaking client-side schema validation, so `resultType`/
|
|
24
|
-
* `CacheableResult` never apply to a tool-call result. The kit's
|
|
25
|
-
* `buildToolCallResult` (0.2.0+) is the conformant builder for that case; the
|
|
26
|
-
* inline `toolResult` helper here predates it and stays for the skeleton's
|
|
27
|
-
* trivial `{ content }` returns.
|
|
28
|
-
*
|
|
29
|
-
* `list_items` is the multi-tenant example tool (tenancy.ts): when a
|
|
30
|
-
* resolved tenant is supplied (OAuth tier — see httpApp.ts), results are
|
|
31
|
-
* scoped via `scopeRowsForTenant` (@vantageos/cloud-identity, reused
|
|
32
|
-
* unmodified); when no tenant is supplied (public tier — no auth gate at
|
|
33
|
-
* all, mirrors stdio's trust model), results are returned unscoped.
|
|
21
|
+
* `list_items` is the multi-tenant example (tenancy.ts): with a resolved tenant, rows are
|
|
22
|
+
* filtered by stored `orgId` (cloud-identity `isRowInTenant`); on the public tier there is no
|
|
23
|
+
* tenant gate at all, by design (it mirrors stdio's local-trust model).
|
|
34
24
|
*/
|
|
35
|
-
import { McpServer } from "@modelcontextprotocol/
|
|
25
|
+
import { McpServer } from "@modelcontextprotocol/server";
|
|
36
26
|
import { type McpLocale } from "./i18n.js";
|
|
37
27
|
import { type ResolvedTenant } from "./tenancy.js";
|
|
28
|
+
/** Oldest 2025-era protocol revision served; the legacy `initialize` handshake never offers older. */
|
|
29
|
+
export declare const SUPPORTED_LEGACY_PROTOCOL_VERSIONS: string[];
|
|
30
|
+
/** The four behavioural annotations, explicit on every tool (all three tools are pure reads). */
|
|
31
|
+
export declare const TOOL_ANNOTATIONS: {
|
|
32
|
+
readonly readOnlyHint: true;
|
|
33
|
+
readonly destructiveHint: false;
|
|
34
|
+
readonly idempotentHint: true;
|
|
35
|
+
readonly openWorldHint: false;
|
|
36
|
+
};
|
|
38
37
|
export interface McpServerDeps {
|
|
39
38
|
/**
|
|
40
|
-
* The caller's resolved tenant (tenancy.ts), when the OAuth tier verified
|
|
41
|
-
*
|
|
42
|
-
*
|
|
39
|
+
* The caller's resolved tenant (tenancy.ts), when the OAuth tier verified a JWT carrying an
|
|
40
|
+
* organization. Undefined on the public tier (no gate) or when the verified token itself
|
|
41
|
+
* carries no organization.
|
|
43
42
|
*/
|
|
44
43
|
tenant?: ResolvedTenant;
|
|
45
44
|
/**
|
|
46
|
-
* True when this deployment's tenant-scoped tools MUST see a resolved
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* falling through to an unscoped/master view. False on the public tier,
|
|
50
|
-
* where there is no tenant gate at all by design.
|
|
45
|
+
* True when this deployment's tenant-scoped tools MUST see a resolved tenant (the OAuth
|
|
46
|
+
* tier). A verified-but-org-less caller then gets an explicit MCP tool-call refusal from
|
|
47
|
+
* `list_items` rather than silently falling through to an unscoped/master view.
|
|
51
48
|
*/
|
|
52
49
|
requireTenant?: boolean;
|
|
53
50
|
/**
|
|
54
|
-
* The caller's locale, resolved from the `X-MCP-Locale` request header
|
|
55
|
-
*
|
|
56
|
-
* stdio transport (no HTTP headers) always uses the default.
|
|
51
|
+
* The caller's locale, resolved from the `X-MCP-Locale` request header (see i18n.ts +
|
|
52
|
+
* httpApp.ts). Defaults to "en"; the stdio transport (no HTTP headers) uses the default.
|
|
57
53
|
*/
|
|
58
54
|
locale?: McpLocale;
|
|
55
|
+
/**
|
|
56
|
+
* Dedicated sandbox origin for the MCP Apps widget (`_meta.ui.domain`). Host-dependent; set
|
|
57
|
+
* only when the deployment has one (MCP_UI_DOMAIN). Omitted from the resource when unset.
|
|
58
|
+
*/
|
|
59
|
+
uiDomain?: string;
|
|
59
60
|
}
|
|
60
61
|
export declare function createBoilerplateMcpServer(deps?: McpServerDeps): McpServer;
|