@fleetless/contracts 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -0
- package/LICENSE +202 -0
- package/NOTICE +17 -0
- package/README.md +88 -0
- package/artifacts/constants.json +24 -0
- package/artifacts/openapi.json +17219 -0
- package/artifacts/routes.json +4605 -0
- package/artifacts/schema/accept-team-invite-request.schema.json +22 -0
- package/artifacts/schema/action-config.schema.json +198 -0
- package/artifacts/schema/alert-list-response.schema.json +172 -0
- package/artifacts/schema/api-error.schema.json +20 -0
- package/artifacts/schema/app-auth-config.schema.json +106 -0
- package/artifacts/schema/app-invitation-list-response.schema.json +57 -0
- package/artifacts/schema/app-invitation.schema.json +69 -0
- package/artifacts/schema/app-list-response.schema.json +82 -0
- package/artifacts/schema/app-mail-template-list-response.schema.json +68 -0
- package/artifacts/schema/app-mail-template.schema.json +54 -0
- package/artifacts/schema/app-oidc-provider-list-response.schema.json +93 -0
- package/artifacts/schema/app-oidc-provider.schema.json +80 -0
- package/artifacts/schema/app-user-list-response.schema.json +111 -0
- package/artifacts/schema/app-user.schema.json +98 -0
- package/artifacts/schema/app.schema.json +69 -0
- package/artifacts/schema/apply-error.schema.json +41 -0
- package/artifacts/schema/asset-list-response.schema.json +288 -0
- package/artifacts/schema/asset-sync-request.schema.json +17 -0
- package/artifacts/schema/asset-sync-response.schema.json +16 -0
- package/artifacts/schema/asset-sync-status.schema.json +136 -0
- package/artifacts/schema/asset.schema.json +68 -0
- package/artifacts/schema/audit-actor.schema.json +32 -0
- package/artifacts/schema/audit-event.schema.json +119 -0
- package/artifacts/schema/audit-list-response.schema.json +144 -0
- package/artifacts/schema/audit-query.schema.json +79 -0
- package/artifacts/schema/auth-error.schema.json +23 -0
- package/artifacts/schema/auth-me-response.schema.json +99 -0
- package/artifacts/schema/auth-ok.schema.json +115 -0
- package/artifacts/schema/authorization-server-metadata.schema.json +80 -0
- package/artifacts/schema/bridge-asset-progress.schema.json +99 -0
- package/artifacts/schema/bridge-assets-available.schema.json +25 -0
- package/artifacts/schema/bridge-camera-state.schema.json +78 -0
- package/artifacts/schema/bridge-config-applied.schema.json +67 -0
- package/artifacts/schema/bridge-hello.schema.json +65 -0
- package/artifacts/schema/bridge-introspect.schema.json +114 -0
- package/artifacts/schema/bridge-job-lost.schema.json +22 -0
- package/artifacts/schema/bridge-job-update.schema.json +100 -0
- package/artifacts/schema/bridge-pong.schema.json +19 -0
- package/artifacts/schema/bridge-pressure.schema.json +292 -0
- package/artifacts/schema/bridge-state.schema.json +24 -0
- package/artifacts/schema/bridge-type-definitions.schema.json +169 -0
- package/artifacts/schema/busy-details.schema.json +115 -0
- package/artifacts/schema/camera-descriptor.schema.json +45 -0
- package/artifacts/schema/camera-list-response.schema.json +58 -0
- package/artifacts/schema/camera-source.schema.json +240 -0
- package/artifacts/schema/cancel-request.schema.json +20 -0
- package/artifacts/schema/client-accept-invitation-request.schema.json +35 -0
- package/artifacts/schema/client-auth.schema.json +18 -0
- package/artifacts/schema/client-cancel.schema.json +45 -0
- package/artifacts/schema/client-identity.schema.json +103 -0
- package/artifacts/schema/client-invoke.schema.json +45 -0
- package/artifacts/schema/client-login-request.schema.json +29 -0
- package/artifacts/schema/client-logout-request.schema.json +14 -0
- package/artifacts/schema/client-mcp-interaction-decision-response.schema.json +15 -0
- package/artifacts/schema/client-mcp-interaction.schema.json +59 -0
- package/artifacts/schema/client-oidc-callback-query.schema.json +28 -0
- package/artifacts/schema/client-oidc-exchange-request.schema.json +21 -0
- package/artifacts/schema/client-oidc-start-query.schema.json +36 -0
- package/artifacts/schema/client-password-reset-confirm-request.schema.json +22 -0
- package/artifacts/schema/client-password-reset-request.schema.json +24 -0
- package/artifacts/schema/client-provider-list-query.schema.json +17 -0
- package/artifacts/schema/client-provider-list-response.schema.json +34 -0
- package/artifacts/schema/client-publish.schema.json +40 -0
- package/artifacts/schema/client-refresh-request.schema.json +14 -0
- package/artifacts/schema/client-register-request.schema.json +44 -0
- package/artifacts/schema/client-resend-verification-request.schema.json +24 -0
- package/artifacts/schema/client-subscribe.schema.json +43 -0
- package/artifacts/schema/client-unsubscribe.schema.json +26 -0
- package/artifacts/schema/client-verify-email-request.schema.json +15 -0
- package/artifacts/schema/cloud-asset-request.schema.json +37 -0
- package/artifacts/schema/cloud-camera-start.schema.json +41 -0
- package/artifacts/schema/cloud-camera-stop.schema.json +26 -0
- package/artifacts/schema/cloud-cancel.schema.json +33 -0
- package/artifacts/schema/cloud-config.schema.json +1635 -0
- package/artifacts/schema/cloud-hello-error.schema.json +23 -0
- package/artifacts/schema/cloud-hello-ok.schema.json +19 -0
- package/artifacts/schema/cloud-introspect-request.schema.json +19 -0
- package/artifacts/schema/cloud-invoke.schema.json +40 -0
- package/artifacts/schema/cloud-ping.schema.json +19 -0
- package/artifacts/schema/cloud-publish.schema.json +28 -0
- package/artifacts/schema/cloud-type-request.schema.json +30 -0
- package/artifacts/schema/command-result.schema.json +175 -0
- package/artifacts/schema/config-draft-response.schema.json +1695 -0
- package/artifacts/schema/config-state.schema.json +124 -0
- package/artifacts/schema/config-version-response.schema.json +1641 -0
- package/artifacts/schema/config-versions-response.schema.json +33 -0
- package/artifacts/schema/create-app-invitation-request.schema.json +40 -0
- package/artifacts/schema/create-app-oidc-provider-request.schema.json +70 -0
- package/artifacts/schema/create-app-request.schema.json +30 -0
- package/artifacts/schema/create-app-user-request.schema.json +42 -0
- package/artifacts/schema/create-robot-request.schema.json +14 -0
- package/artifacts/schema/create-robot-response.schema.json +44 -0
- package/artifacts/schema/create-server-key-response.schema.json +65 -0
- package/artifacts/schema/create-team-invite-request.schema.json +43 -0
- package/artifacts/schema/datapoint-alert-row.schema.json +160 -0
- package/artifacts/schema/datapoint-config.schema.json +366 -0
- package/artifacts/schema/datapoint-display.schema.json +31 -0
- package/artifacts/schema/datapoint-event.schema.json +34 -0
- package/artifacts/schema/datapoint-frame.schema.json +28 -0
- package/artifacts/schema/datapoint-list-response.schema.json +61 -0
- package/artifacts/schema/datapoint-value.schema.json +28 -0
- package/artifacts/schema/developer-login-request.schema.json +19 -0
- package/artifacts/schema/dynamic-client-registration-request.schema.json +60 -0
- package/artifacts/schema/dynamic-client-registration-response.schema.json +68 -0
- package/artifacts/schema/error-frame.schema.json +23 -0
- package/artifacts/schema/exposure-counts.schema.json +39 -0
- package/artifacts/schema/exposure-list-response.schema.json +43 -0
- package/artifacts/schema/fetch-types-request.schema.json +19 -0
- package/artifacts/schema/fetch-types-response.schema.json +163 -0
- package/artifacts/schema/fleetless-user-list-response.schema.json +73 -0
- package/artifacts/schema/fleetless-user.schema.json +60 -0
- package/artifacts/schema/history-buckets-response.schema.json +79 -0
- package/artifacts/schema/history-query.schema.json +58 -0
- package/artifacts/schema/history-response.schema.json +150 -0
- package/artifacts/schema/history-samples-response.schema.json +68 -0
- package/artifacts/schema/introspection-response.schema.json +118 -0
- package/artifacts/schema/invoke-or-service-response.schema.json +141 -0
- package/artifacts/schema/invoke-request.schema.json +23 -0
- package/artifacts/schema/invoke-response.schema.json +125 -0
- package/artifacts/schema/job-actor.schema.json +34 -0
- package/artifacts/schema/job-event.schema.json +158 -0
- package/artifacts/schema/job-response.schema.json +123 -0
- package/artifacts/schema/job-run-list-response.schema.json +222 -0
- package/artifacts/schema/job-run-query.schema.json +95 -0
- package/artifacts/schema/job-run-summary-query.schema.json +23 -0
- package/artifacts/schema/job-run-summary.schema.json +33 -0
- package/artifacts/schema/job-run.schema.json +195 -0
- package/artifacts/schema/job-state.schema.json +11 -0
- package/artifacts/schema/job.schema.json +106 -0
- package/artifacts/schema/latency-bucket.schema.json +63 -0
- package/artifacts/schema/live-session-response.schema.json +41 -0
- package/artifacts/schema/mail-outcome.schema.json +20 -0
- package/artifacts/schema/mail-template-preview-request.schema.json +35 -0
- package/artifacts/schema/mail-template-preview-response.schema.json +31 -0
- package/artifacts/schema/mail-template-problem-details.schema.json +24 -0
- package/artifacts/schema/mcp-consent-grant-list-response.schema.json +52 -0
- package/artifacts/schema/mcp-consent-grant.schema.json +39 -0
- package/artifacts/schema/mcp-robot-datasheet.schema.json +115 -0
- package/artifacts/schema/mcp-role-preview-response.schema.json +134 -0
- package/artifacts/schema/missing-asset-query.schema.json +11 -0
- package/artifacts/schema/oauth-authorize-query.schema.json +47 -0
- package/artifacts/schema/oauth-redirect-response.schema.json +15 -0
- package/artifacts/schema/oauth-token-request.schema.json +47 -0
- package/artifacts/schema/oauth-token-response.schema.json +38 -0
- package/artifacts/schema/org-alerts-query.schema.json +15 -0
- package/artifacts/schema/org-event-dropped.schema.json +26 -0
- package/artifacts/schema/org-event-replay.schema.json +97 -0
- package/artifacts/schema/org-event-subscribe.schema.json +14 -0
- package/artifacts/schema/org-event-unsubscribe.schema.json +14 -0
- package/artifacts/schema/org-event.schema.json +75 -0
- package/artifacts/schema/org-firing-alerts-response.schema.json +178 -0
- package/artifacts/schema/org-health-query.schema.json +13 -0
- package/artifacts/schema/org-latency-query.schema.json +42 -0
- package/artifacts/schema/org-latency-response.schema.json +124 -0
- package/artifacts/schema/org-quota-usage-counts.schema.json +42 -0
- package/artifacts/schema/org-quota-usage.schema.json +102 -0
- package/artifacts/schema/org-quotas.schema.json +51 -0
- package/artifacts/schema/org-usage-query.schema.json +19 -0
- package/artifacts/schema/org-usage-response.schema.json +77 -0
- package/artifacts/schema/org.schema.json +30 -0
- package/artifacts/schema/parameter-invalid-details.schema.json +37 -0
- package/artifacts/schema/parameter-spec.schema.json +120 -0
- package/artifacts/schema/parameter-violation.schema.json +24 -0
- package/artifacts/schema/password-change-request.schema.json +21 -0
- package/artifacts/schema/password-reset-confirm.schema.json +19 -0
- package/artifacts/schema/password-reset-request.schema.json +14 -0
- package/artifacts/schema/patch-app-oidc-provider-request.schema.json +50 -0
- package/artifacts/schema/patch-app-user-request.schema.json +34 -0
- package/artifacts/schema/patch-auth-me-request.schema.json +22 -0
- package/artifacts/schema/patch-fleetless-user-request.schema.json +20 -0
- package/artifacts/schema/patch-org-request.schema.json +15 -0
- package/artifacts/schema/patch-org-response.schema.json +40 -0
- package/artifacts/schema/patch-robot-request.schema.json +15 -0
- package/artifacts/schema/patch-robot-response.schema.json +40 -0
- package/artifacts/schema/pending-team-invite-list-response.schema.json +52 -0
- package/artifacts/schema/pending-team-invite.schema.json +39 -0
- package/artifacts/schema/protected-resource-metadata.schema.json +41 -0
- package/artifacts/schema/publish-config-response.schema.json +21 -0
- package/artifacts/schema/publish-request.schema.json +17 -0
- package/artifacts/schema/publisher-config.schema.json +285 -0
- package/artifacts/schema/put-app-auth-config-request.schema.json +93 -0
- package/artifacts/schema/put-app-mail-template-request.schema.json +35 -0
- package/artifacts/schema/put-config-draft-request.schema.json +13 -0
- package/artifacts/schema/put-datapoint-display-request.schema.json +31 -0
- package/artifacts/schema/put-robot-details-request.schema.json +41 -0
- package/artifacts/schema/put-robot-details-response.schema.json +43 -0
- package/artifacts/schema/rate-limit-details.schema.json +15 -0
- package/artifacts/schema/refresh-request.schema.json +13 -0
- package/artifacts/schema/release-live-query.schema.json +13 -0
- package/artifacts/schema/rename-slug-request.schema.json +23 -0
- package/artifacts/schema/rename-slug-response.schema.json +24 -0
- package/artifacts/schema/resource-health-event.schema.json +72 -0
- package/artifacts/schema/resource-health-list-response.schema.json +80 -0
- package/artifacts/schema/resource-health-state.schema.json +68 -0
- package/artifacts/schema/robot-config-doc.schema.json +1616 -0
- package/artifacts/schema/robot-delete-query.schema.json +12 -0
- package/artifacts/schema/robot-deletion-summary.schema.json +63 -0
- package/artifacts/schema/robot-detail-response.schema.json +262 -0
- package/artifacts/schema/robot-details-doc.schema.json +33 -0
- package/artifacts/schema/robot-jobs-response.schema.json +119 -0
- package/artifacts/schema/robot-latency-series.schema.json +81 -0
- package/artifacts/schema/robot-list-item.schema.json +94 -0
- package/artifacts/schema/robot-list-response.schema.json +106 -0
- package/artifacts/schema/robot.schema.json +30 -0
- package/artifacts/schema/role-list-response.schema.json +48 -0
- package/artifacts/schema/role-permissions.schema.json +61 -0
- package/artifacts/schema/role.schema.json +35 -0
- package/artifacts/schema/ros-graph.schema.json +99 -0
- package/artifacts/schema/server-key-list-response.schema.json +64 -0
- package/artifacts/schema/server-key.schema.json +51 -0
- package/artifacts/schema/service-call-response.schema.json +13 -0
- package/artifacts/schema/service-config.schema.json +198 -0
- package/artifacts/schema/session-tokens.schema.json +28 -0
- package/artifacts/schema/sign-up-request.schema.json +26 -0
- package/artifacts/schema/sign-up-response.schema.json +127 -0
- package/artifacts/schema/slug-usage-response.schema.json +32 -0
- package/artifacts/schema/snapshot-header.schema.json +44 -0
- package/artifacts/schema/snapshot-meta-response.schema.json +85 -0
- package/artifacts/schema/subscribe-error.schema.json +31 -0
- package/artifacts/schema/team-invite.schema.json +57 -0
- package/artifacts/schema/tier-change-request.schema.json +17 -0
- package/artifacts/schema/type-definition.schema.json +144 -0
- package/artifacts/schema/types-response.schema.json +156 -0
- package/artifacts/schema/update-app-request.schema.json +32 -0
- package/artifacts/schema/urdf-completeness.schema.json +50 -0
- package/artifacts/schema/validation-issue.schema.json +43 -0
- package/artifacts/schema/waitlist-request.schema.json +15 -0
- package/artifacts/schema-outgoing/bridge-asset-progress.schema.json +102 -0
- package/artifacts/schema-outgoing/bridge-assets-available.schema.json +26 -0
- package/artifacts/schema-outgoing/bridge-camera-state.schema.json +80 -0
- package/artifacts/schema-outgoing/bridge-config-applied.schema.json +69 -0
- package/artifacts/schema-outgoing/bridge-hello.schema.json +68 -0
- package/artifacts/schema-outgoing/bridge-introspect.schema.json +119 -0
- package/artifacts/schema-outgoing/bridge-job-lost.schema.json +23 -0
- package/artifacts/schema-outgoing/bridge-job-update.schema.json +102 -0
- package/artifacts/schema-outgoing/bridge-pong.schema.json +20 -0
- package/artifacts/schema-outgoing/bridge-type-definitions.schema.json +174 -0
- package/artifacts/schema-outgoing/datapoint-frame.schema.json +29 -0
- package/artifacts/schema-outgoing/snapshot-header.schema.json +45 -0
- package/dist/alerts.d.ts +255 -0
- package/dist/alerts.js +193 -0
- package/dist/app-users.d.ts +606 -0
- package/dist/app-users.js +696 -0
- package/dist/apps.d.ts +175 -0
- package/dist/apps.js +267 -0
- package/dist/assets.d.ts +434 -0
- package/dist/assets.js +546 -0
- package/dist/audit.d.ts +129 -0
- package/dist/audit.js +238 -0
- package/dist/client-auth.d.ts +409 -0
- package/dist/client-auth.js +487 -0
- package/dist/common.d.ts +186 -0
- package/dist/common.js +199 -0
- package/dist/config-issues.d.ts +175 -0
- package/dist/config-issues.js +339 -0
- package/dist/config.d.ts +862 -0
- package/dist/config.js +1988 -0
- package/dist/errors.d.ts +52 -0
- package/dist/errors.js +786 -0
- package/dist/identity.d.ts +549 -0
- package/dist/identity.js +503 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +51 -0
- package/dist/introspection.d.ts +99 -0
- package/dist/introspection.js +97 -0
- package/dist/jobs.d.ts +334 -0
- package/dist/jobs.js +345 -0
- package/dist/mcp.d.ts +239 -0
- package/dist/mcp.js +153 -0
- package/dist/oauth.d.ts +344 -0
- package/dist/oauth.js +488 -0
- package/dist/protocol.d.ts +781 -0
- package/dist/protocol.js +715 -0
- package/dist/realtime.d.ts +494 -0
- package/dist/realtime.js +512 -0
- package/dist/rest.d.ts +1989 -0
- package/dist/rest.js +1963 -0
- package/dist/routes.d.ts +94 -0
- package/dist/routes.js +2298 -0
- package/package.json +61 -0
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { slug } from './common.js';
|
|
4
|
+
/**
|
|
5
|
+
* The MCP server of §17: **one** remote MCP endpoint for the whole platform,
|
|
6
|
+
* whose tools are the exposed services and datapoints the signed-in user's
|
|
7
|
+
* roles permit. The per-app `/mcp/<identifier>` servers this file once
|
|
8
|
+
* described were deleted by the org-central identity redesign (D5/D6).
|
|
9
|
+
*
|
|
10
|
+
* **This file describes the seam, not the protocol.** The MCP messages
|
|
11
|
+
* themselves (`initialize`, `tools/list`, `tools/call`) are defined by the
|
|
12
|
+
* Model Context Protocol and implemented with its official SDK — writing our
|
|
13
|
+
* own zod copies of them would create a second source of truth for somebody
|
|
14
|
+
* else's specification, which is the one thing this package exists to avoid.
|
|
15
|
+
* What lives here is what *Fleetless* decides: which revision we speak, where
|
|
16
|
+
* the endpoint is, how a tool is named, and what the console is shown before
|
|
17
|
+
* an end user ever connects.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* The protocol revision W7c speaks. Chosen with André on 2026-08-18 over the
|
|
21
|
+
* newer `2026-07-28`.
|
|
22
|
+
*
|
|
23
|
+
* This is the latest revision the **stable** MCP TypeScript SDK ships, and it
|
|
24
|
+
* negotiates down to `2024-11-05`, so it covers the AI tools that exist today.
|
|
25
|
+
* `2026-07-28` is real and is where the protocol is going — it *removes*
|
|
26
|
+
* Streamable HTTP's session ids, the standalone SSE channel and resumability,
|
|
27
|
+
* and servers speaking only it answer `405` to GET and DELETE.
|
|
28
|
+
*
|
|
29
|
+
* **Which is why this server is stateless anyway.** Building sessions we would
|
|
30
|
+
* have to delete again is work in the wrong direction, and a per-process
|
|
31
|
+
* session map is the assumption that breaks at the second cloud instance —
|
|
32
|
+
* the register already carries one row of exactly that shape
|
|
33
|
+
* (`max_realtime_connections`), and W8 is where a second instance appears.
|
|
34
|
+
*/
|
|
35
|
+
export const MCP_PROTOCOL_VERSION = '2025-11-25';
|
|
36
|
+
/**
|
|
37
|
+
* The path of the **central** MCP server — what a *Fleetless user* pastes into
|
|
38
|
+
* their AI tool, appended to the cloud's public base URL.
|
|
39
|
+
*
|
|
40
|
+
* **Not parameterised, and that is now a statement rather than the absence of
|
|
41
|
+
* one.** The central endpoint serves the org's team with the console tool
|
|
42
|
+
* family (2026-09-05, D7); an app's users reach a different endpoint, whose
|
|
43
|
+
* path `mcpAppEndpointPath` builds. Two constants for two audiences, so a call
|
|
44
|
+
* site says which it means instead of an argument deciding it.
|
|
45
|
+
*
|
|
46
|
+
* **The canonical URL is `<PUBLIC_API_BASE_URL>${MCP_ENDPOINT_PATH}`, not the
|
|
47
|
+
* friendly alias.** `mcp.fleetless.dev` is a reverse proxy onto the same
|
|
48
|
+
* cloud, but the cloud mints every OAuth issuer, resource and `aud` from
|
|
49
|
+
* `PUBLIC_API_BASE_URL` and compares the token's `aud` against that string —
|
|
50
|
+
* never against the request's `Host`. Hand out the canonical one.
|
|
51
|
+
*/
|
|
52
|
+
export const MCP_ENDPOINT_PATH = '/mcp';
|
|
53
|
+
/**
|
|
54
|
+
* The path of **one app's** MCP server (D7) — what an app user pastes into
|
|
55
|
+
* their AI tool, served only while the app's `appAuthConfig.mcp_enabled` is on.
|
|
56
|
+
*
|
|
57
|
+
* A helper rather than a template literal at four call sites, for
|
|
58
|
+
* `OAUTH_PATHS`' reason: the console shows this string with a copy button, the
|
|
59
|
+
* cloud registers the route from it, and the docs render it. A path spelled in
|
|
60
|
+
* three places is a path two of them will one day spell differently — and this
|
|
61
|
+
* repository has already paid for exactly that, with an `idpStart` entry naming
|
|
62
|
+
* a route the cloud had deleted.
|
|
63
|
+
*
|
|
64
|
+
* **This is the path, not the URL.** Append it to `PUBLIC_API_BASE_URL`, the
|
|
65
|
+
* canonical origin the cloud mints every issuer and audience from, rather than
|
|
66
|
+
* to the friendly `mcp.fleetless.dev` alias — a token's `aud` is compared
|
|
67
|
+
* against the canonical string and never against the request's `Host`.
|
|
68
|
+
*
|
|
69
|
+
* There was a `mcpEndpointPath(appIdentifier)` before, deleted with the per-app
|
|
70
|
+
* endpoint in the central-MCP cut and remembered here because the shape of that
|
|
71
|
+
* mistake is worth not repeating: the console kept offering a copy button for a
|
|
72
|
+
* URL that answered `404`. This one exists **with** its endpoint, and the
|
|
73
|
+
* cloud's route-manifest test is what keeps them together.
|
|
74
|
+
*/
|
|
75
|
+
export function mcpAppEndpointPath(appIdentifier) {
|
|
76
|
+
return `/mcp/${appIdentifier}`;
|
|
77
|
+
}
|
|
78
|
+
export function MCP_APP_PATHS(appIdentifier) {
|
|
79
|
+
const endpoint = mcpAppEndpointPath(appIdentifier);
|
|
80
|
+
return {
|
|
81
|
+
endpoint,
|
|
82
|
+
protectedResourceMetadata: `/.well-known/oauth-protected-resource${endpoint}`,
|
|
83
|
+
authorizationServerMetadata: `/.well-known/oauth-authorization-server${endpoint}`,
|
|
84
|
+
register: `${endpoint}/oauth/register`,
|
|
85
|
+
authorize: `${endpoint}/oauth/authorize`,
|
|
86
|
+
token: `${endpoint}/oauth/token`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Which exposed kind a tool came from. Not the MCP protocol's vocabulary —
|
|
91
|
+
* ours, so the console can group a preview the way the services editor is
|
|
92
|
+
* grouped.
|
|
93
|
+
*/
|
|
94
|
+
export const mcpToolKind = z.enum(['datapoint', 'service', 'action', 'publisher', 'camera']);
|
|
95
|
+
/** MCP's own bound on a tool name, and the charset that is safe across clients. */
|
|
96
|
+
export const MCP_TOOL_NAME_MAX = 128;
|
|
97
|
+
export const mcpToolNamePattern = /^[a-z0-9][a-z0-9_-]*$/;
|
|
98
|
+
/**
|
|
99
|
+
* One exposure of a robot, as `robot_describe` and the console's per-role
|
|
100
|
+
* preview list it (FL-006). Every exposure the role grants is listed —
|
|
101
|
+
* a missing `description` is shown as `null`, never used to hide the entry.
|
|
102
|
+
*
|
|
103
|
+
* `input_schema` is a JSON Schema document generated from an action's,
|
|
104
|
+
* service's or publisher's `parameters`; `null` for the other kinds. It is
|
|
105
|
+
* `unknown` for the same reason the retired `mcpToolPreview.input_schema`
|
|
106
|
+
* was: pinning it would mean maintaining a zod description of JSON Schema.
|
|
107
|
+
*/
|
|
108
|
+
export const mcpExposure = z.object({
|
|
109
|
+
slug,
|
|
110
|
+
kind: mcpToolKind,
|
|
111
|
+
description: z.string().max(2000).nullable(),
|
|
112
|
+
/** A datapoint's `numeric.unit`, verbatim; `null` for every other kind and for a unitless datapoint. */
|
|
113
|
+
unit: z.string().max(32).nullable(),
|
|
114
|
+
/**
|
|
115
|
+
* A datapoint's `numeric.decimals`, verbatim; `null` for every other kind
|
|
116
|
+
* and for a datapoint that does not set it. Required-nullable rather than
|
|
117
|
+
* optional for the same reason as `unit`: an omitted field would make a
|
|
118
|
+
* producer that forgot the datapoint's configuration indistinguishable from
|
|
119
|
+
* one reporting a datapoint that has none.
|
|
120
|
+
*/
|
|
121
|
+
decimals: z.number().int().min(0).max(6).nullable(),
|
|
122
|
+
input_schema: z.unknown().nullable(),
|
|
123
|
+
});
|
|
124
|
+
/** The two role capabilities a robot tool can need beyond a slug grant. `presence` is a stream and has no tool. */
|
|
125
|
+
export const mcpCapabilities = z.object({
|
|
126
|
+
action_history: z.boolean(),
|
|
127
|
+
assets: z.boolean(),
|
|
128
|
+
});
|
|
129
|
+
/** What one caller may do on one robot — the answer to `robot_describe`. */
|
|
130
|
+
export const mcpRobotDatasheet = z.object({
|
|
131
|
+
robot_id: z.uuid(),
|
|
132
|
+
robot_name: z.string().min(1).max(200),
|
|
133
|
+
capabilities: mcpCapabilities,
|
|
134
|
+
exposures: z.array(mcpExposure).max(2000),
|
|
135
|
+
});
|
|
136
|
+
/**
|
|
137
|
+
* What a developer sees before an end user connects: the datasheet each
|
|
138
|
+
* robot of the app would answer for one role. Replaces the per-slug tool
|
|
139
|
+
* preview and its `omitted` list — with a fixed catalog there is no tool to
|
|
140
|
+
* omit, only exposures to grant.
|
|
141
|
+
*/
|
|
142
|
+
export const mcpRolePreviewResponse = z.object({
|
|
143
|
+
role_id: z.uuid(),
|
|
144
|
+
robots: z.array(mcpRobotDatasheet).max(500),
|
|
145
|
+
});
|
|
146
|
+
/**
|
|
147
|
+
* Where a signed asset link is served. An MCP session token is refused on
|
|
148
|
+
* REST by design, so `asset_get`/`urdf_get` mint a bearer-free link the agent
|
|
149
|
+
* behind the client can fetch. Lifetime is fixed; the token binds robot, asset
|
|
150
|
+
* and expiry under `JWT_SECRET`.
|
|
151
|
+
*/
|
|
152
|
+
export const MCP_ASSET_LINK_PATH = '/api/asset-links';
|
|
153
|
+
export const MCP_ASSET_LINK_TTL_MS = 15 * 60 * 1000;
|
package/dist/oauth.d.ts
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* **OAuth 2.1, and it remains only for MCP** (2026-09-05 app-user-auth, D8).
|
|
4
|
+
*
|
|
5
|
+
* This file used to describe two front doors: an app's end users signing in
|
|
6
|
+
* through a Fleetless-hosted, app-branded login page, and MCP clients signing
|
|
7
|
+
* in for the central endpoint. The first is deleted. An app now has its own UI
|
|
8
|
+
* and calls the JSON client-auth API (`client-auth.ts`); Fleetless renders an
|
|
9
|
+
* app user no page, so there is no hosted login, no consent screen, no
|
|
10
|
+
* developer-registered client and no app-level dynamic registration.
|
|
11
|
+
*
|
|
12
|
+
* What remains is the MCP authorization server — central, for Fleetless users,
|
|
13
|
+
* and per app for an app's users — and the console's own OAuth portal, which
|
|
14
|
+
* answers `oauthRedirectResponse` at its login and sign-up steps.
|
|
15
|
+
*
|
|
16
|
+
* The client model this file was written to get right is still the important
|
|
17
|
+
* part, and it survived the cut intact: an MCP client **registers itself**
|
|
18
|
+
* (RFC 7591) because the person only ever pastes a URL into an AI tool.
|
|
19
|
+
* Nobody vetted it, its redirect URIs arrive from the client itself, and the
|
|
20
|
+
* tools it will call move a physical robot. That is why consent names the
|
|
21
|
+
* client with an explicit *unverified* marker — see `clientMcpInteraction` in
|
|
22
|
+
* `client-auth.ts`, which is where the per-app half of that screen is now
|
|
23
|
+
* described, because the app renders it and Fleetless does not.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* **This file speaks two error dialects on purpose, and unifying them would
|
|
27
|
+
* break conformance.**
|
|
28
|
+
*
|
|
29
|
+
* The OAuth endpoints (`/mcp/oauth/authorize`, `/mcp/oauth/token`,
|
|
30
|
+
* registration) answer in RFC 6749 §5.2's shape — a flat `error` string from a fixed set,
|
|
31
|
+
* with an optional `error_description`. That is what an RFC-compliant client
|
|
32
|
+
* parses, and `mcp-inspector` is such a client. A Fleetless `apiError` there
|
|
33
|
+
* would be well-formed JSON that no standard client can read.
|
|
34
|
+
*
|
|
35
|
+
* The *management* endpoints beside them — configuring a provider, editing an
|
|
36
|
+
* app's auth settings — are ordinary console API and use `apiError` with
|
|
37
|
+
* `ERROR_CODES` like everything else.
|
|
38
|
+
*
|
|
39
|
+
* So: **two shapes, split by audience, not by accident.** Written down here
|
|
40
|
+
* because the natural instinct on finding two error formats in one server is
|
|
41
|
+
* to unify them, and doing so silently removes the reason the standard one is
|
|
42
|
+
* there.
|
|
43
|
+
*
|
|
44
|
+
* **The split is by audience and the path prefix will mislead you.**
|
|
45
|
+
* `/mcp/oauth/consent` sits under an `/oauth/` segment and is nevertheless an
|
|
46
|
+
* `apiError` endpoint: it is not in RFC 6749's or RFC 7591's endpoint set, and
|
|
47
|
+
* its only caller is a page this server rendered. Whoever later sorts these by
|
|
48
|
+
* prefix will move it, and be wrong. Ask who parses the response, not where it
|
|
49
|
+
* lives.
|
|
50
|
+
*/
|
|
51
|
+
export declare const oauthErrorCode: z.ZodEnum<{
|
|
52
|
+
invalid_request: "invalid_request";
|
|
53
|
+
invalid_client: "invalid_client";
|
|
54
|
+
invalid_grant: "invalid_grant";
|
|
55
|
+
unauthorized_client: "unauthorized_client";
|
|
56
|
+
unsupported_grant_type: "unsupported_grant_type";
|
|
57
|
+
invalid_scope: "invalid_scope";
|
|
58
|
+
access_denied: "access_denied";
|
|
59
|
+
server_error: "server_error";
|
|
60
|
+
temporarily_unavailable: "temporarily_unavailable";
|
|
61
|
+
invalid_target: "invalid_target";
|
|
62
|
+
}>;
|
|
63
|
+
export type OauthErrorCode = z.infer<typeof oauthErrorCode>;
|
|
64
|
+
export declare const oauthError: z.ZodObject<{
|
|
65
|
+
error: z.ZodEnum<{
|
|
66
|
+
invalid_request: "invalid_request";
|
|
67
|
+
invalid_client: "invalid_client";
|
|
68
|
+
invalid_grant: "invalid_grant";
|
|
69
|
+
unauthorized_client: "unauthorized_client";
|
|
70
|
+
unsupported_grant_type: "unsupported_grant_type";
|
|
71
|
+
invalid_scope: "invalid_scope";
|
|
72
|
+
access_denied: "access_denied";
|
|
73
|
+
server_error: "server_error";
|
|
74
|
+
temporarily_unavailable: "temporarily_unavailable";
|
|
75
|
+
invalid_target: "invalid_target";
|
|
76
|
+
}>;
|
|
77
|
+
error_description: z.ZodOptional<z.ZodString>;
|
|
78
|
+
state: z.ZodOptional<z.ZodString>;
|
|
79
|
+
fleetless_code: z.ZodOptional<z.ZodString>;
|
|
80
|
+
}, z.core.$strip>;
|
|
81
|
+
export type OauthError = z.infer<typeof oauthError>;
|
|
82
|
+
/**
|
|
83
|
+
* A redirect URI, and the rule is stricter than "a URL".
|
|
84
|
+
*
|
|
85
|
+
* **The defence for this was already written down in this codebase, twice.**
|
|
86
|
+
* `config.ts` validates a V4L2 device path from the wire with a prefix rule
|
|
87
|
+
* *and* an explicit refusal of `..` segments, tested, with the reasoning
|
|
88
|
+
* recorded; W7's review then found a `package://` traversal in the bridge
|
|
89
|
+
* that the same rule would have prevented, and the finding that mattered was
|
|
90
|
+
* not the traversal but that **the rule existed one file over and was never
|
|
91
|
+
* carried across.** A redirect URI is the same shape of problem from a less
|
|
92
|
+
* trusted source: an attacker-supplied string that decides where a credential
|
|
93
|
+
* is sent.
|
|
94
|
+
*
|
|
95
|
+
* Matching at the server is **exact string comparison against a registered
|
|
96
|
+
* value** — never a prefix, never a wildcard host, never "starts with". A
|
|
97
|
+
* prefix match on `https://app.example.com/cb` accepts
|
|
98
|
+
* `https://app.example.com/cb.evil.test`.
|
|
99
|
+
*/
|
|
100
|
+
export declare const redirectUri: z.ZodString;
|
|
101
|
+
export type RedirectUri = z.infer<typeof redirectUri>;
|
|
102
|
+
/**
|
|
103
|
+
* OAuth 2.1 removes the implicit and password grants and **makes PKCE
|
|
104
|
+
* mandatory for every client**, public or confidential. `plain` is not
|
|
105
|
+
* offered: a challenge equal to its verifier defends against nothing, and
|
|
106
|
+
* offering it means a downgrade is negotiable.
|
|
107
|
+
*/
|
|
108
|
+
export declare const codeChallengeMethod: z.ZodEnum<{
|
|
109
|
+
S256: "S256";
|
|
110
|
+
}>;
|
|
111
|
+
/**
|
|
112
|
+
* **How many callbacks one dynamic registration may name.**
|
|
113
|
+
*
|
|
114
|
+
* RFC 7591 lets a client register several; five is above every real MCP client
|
|
115
|
+
* observed and far below "a place to store data" on an endpoint that takes no
|
|
116
|
+
* credential. It lives here rather than in the cloud because this schema now
|
|
117
|
+
* *publishes* the bound: a number the reference states and a different number
|
|
118
|
+
* the server enforces is two policies for one decision, and the endpoint spent
|
|
119
|
+
* a release documenting `20` while refusing the sixth URI.
|
|
120
|
+
*/
|
|
121
|
+
export declare const MCP_DCR_MAX_REDIRECT_URIS = 5;
|
|
122
|
+
/**
|
|
123
|
+
* RFC 7591 dynamic client registration — **the metadata both MCP
|
|
124
|
+
* authorization servers understand**, central and per-app.
|
|
125
|
+
*
|
|
126
|
+
* **Not `.strict()`, and that is the schema agreeing with the server rather
|
|
127
|
+
* than a gap in it.** §3.1 obliges a registration endpoint to ignore metadata
|
|
128
|
+
* it does not understand, and real MCP clients send `client_uri`, `logo_uri`,
|
|
129
|
+
* `software_id` and `contacts`. A strict shape here would describe a `400`
|
|
130
|
+
* that no conforming client ever earns, and would take the whole
|
|
131
|
+
* paste-the-URL flow down if anything ever parsed against it. Unknown keys
|
|
132
|
+
* are therefore stripped by this schema and ignored by the server, which is
|
|
133
|
+
* the same answer said twice.
|
|
134
|
+
*
|
|
135
|
+
* **The server still reads the body field by field** (`registerMcpDynamicClient`
|
|
136
|
+
* in `cloud/src/mcp-oauth-core.ts`), and the reason is the error vocabulary,
|
|
137
|
+
* not the shape: §3.2.2 distinguishes `invalid_redirect_uri` from
|
|
138
|
+
* `invalid_client_metadata`, and one `safeParse` failure cannot say which of
|
|
139
|
+
* the two a caller earned. So this schema is what the endpoint *accepts*, and
|
|
140
|
+
* the handler is what turns a miss into the right RFC code.
|
|
141
|
+
*
|
|
142
|
+
* **`client_name` is optional because the server treats it as optional**: RFC
|
|
143
|
+
* 7591 makes every metadata field optional, and a registration that omits it
|
|
144
|
+
* is recorded under a default name rather than refused. `redirect_uris` is the
|
|
145
|
+
* one field a registration cannot do without — there is nowhere to return a
|
|
146
|
+
* code otherwise.
|
|
147
|
+
*/
|
|
148
|
+
export declare const dynamicClientRegistrationRequest: z.ZodObject<{
|
|
149
|
+
redirect_uris: z.ZodArray<z.ZodString>;
|
|
150
|
+
client_name: z.ZodOptional<z.ZodString>;
|
|
151
|
+
token_endpoint_auth_method: z.ZodOptional<z.ZodEnum<{
|
|
152
|
+
none: "none";
|
|
153
|
+
}>>;
|
|
154
|
+
grant_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
155
|
+
refresh_token: "refresh_token";
|
|
156
|
+
authorization_code: "authorization_code";
|
|
157
|
+
}>>>;
|
|
158
|
+
response_types: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
159
|
+
code: "code";
|
|
160
|
+
}>>>;
|
|
161
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
162
|
+
}, z.core.$strip>;
|
|
163
|
+
export type DynamicClientRegistrationRequest = z.infer<typeof dynamicClientRegistrationRequest>;
|
|
164
|
+
export declare const dynamicClientRegistrationResponse: z.ZodObject<{
|
|
165
|
+
client_id: z.ZodString;
|
|
166
|
+
client_name: z.ZodString;
|
|
167
|
+
redirect_uris: z.ZodArray<z.ZodString>;
|
|
168
|
+
grant_types: z.ZodArray<z.ZodString>;
|
|
169
|
+
response_types: z.ZodArray<z.ZodString>;
|
|
170
|
+
token_endpoint_auth_method: z.ZodLiteral<"none">;
|
|
171
|
+
client_id_issued_at: z.ZodNumber;
|
|
172
|
+
client_secret_expires_at: z.ZodLiteral<0>;
|
|
173
|
+
}, z.core.$strip>;
|
|
174
|
+
export type DynamicClientRegistrationResponse = z.infer<typeof dynamicClientRegistrationResponse>;
|
|
175
|
+
/**
|
|
176
|
+
* **The MCP token endpoint's request — one grant, because the servers serve
|
|
177
|
+
* one.**
|
|
178
|
+
*
|
|
179
|
+
* Both authorization servers, central and per-app, exchange through
|
|
180
|
+
* `exchangeMcpAuthorizationCode` (`cloud/src/mcp-oauth-core.ts`), whose first
|
|
181
|
+
* act is to refuse anything but `authorization_code` before a single lookup
|
|
182
|
+
* happens. There is no refresh grant here: a session ends when its token
|
|
183
|
+
* expires and the client signs in again.
|
|
184
|
+
*
|
|
185
|
+
* **This was a `discriminatedUnion` with a `refresh_token` branch, and that
|
|
186
|
+
* branch had no producer left.** It described the app-level OAuth surface,
|
|
187
|
+
* which is deleted; an app user's refresh runs through `POST
|
|
188
|
+
* /api/client/refresh` and `refreshRequest`, a different wire on a different
|
|
189
|
+
* route. Keeping it would have published, to every MCP client author reading
|
|
190
|
+
* `/openapi.json`, a grant the endpoint answers `unsupported_grant_type` to.
|
|
191
|
+
* The argument the branch carried is worth keeping even though the branch is
|
|
192
|
+
* not: **RFC 8707's `resource` has to survive rotation**, because a refresh
|
|
193
|
+
* that drops the audience mints a successor with no `aud`, and the validating
|
|
194
|
+
* resource then refuses a token the caller obtained legitimately — one token
|
|
195
|
+
* lifetime after a login that worked, to somebody who did nothing wrong. If a
|
|
196
|
+
* refresh grant is ever added here, it carries `resource`.
|
|
197
|
+
*
|
|
198
|
+
* `code_verifier`'s bounds are RFC 7636 §4.1's, charset included. A verifier
|
|
199
|
+
* is compared, not parsed, so a length nobody checks is a length an attacker
|
|
200
|
+
* chooses.
|
|
201
|
+
*
|
|
202
|
+
* **Deliberately not `.strict()`**, unlike a registration request: that comes
|
|
203
|
+
* from a client we are about to trust, where an unknown key is a caller
|
|
204
|
+
* assuming a feature into existence, while a token request comes from any
|
|
205
|
+
* RFC-compliant client, which may legitimately send parameters this server
|
|
206
|
+
* does not read. Refusing those would be a conformance bug. The consequence
|
|
207
|
+
* is worth stating because it bit the test for this very schema: unknown keys
|
|
208
|
+
* are **stripped**, so `safeParse().success` cannot tell a present field from
|
|
209
|
+
* an absent one. Assert on the parsed value.
|
|
210
|
+
*/
|
|
211
|
+
export declare const oauthTokenRequest: z.ZodObject<{
|
|
212
|
+
grant_type: z.ZodLiteral<"authorization_code">;
|
|
213
|
+
code: z.ZodString;
|
|
214
|
+
redirect_uri: z.ZodString;
|
|
215
|
+
client_id: z.ZodString;
|
|
216
|
+
code_verifier: z.ZodString;
|
|
217
|
+
resource: z.ZodOptional<z.ZodURL>;
|
|
218
|
+
}, z.core.$strip>;
|
|
219
|
+
export type OauthTokenRequest = z.infer<typeof oauthTokenRequest>;
|
|
220
|
+
/**
|
|
221
|
+
* RFC 6749 §5.1's success envelope — **the second deliberate dialect, and this
|
|
222
|
+
* one is a success shape rather than an error shape.**
|
|
223
|
+
*
|
|
224
|
+
* The values inside are the same tokens `/api/client/login` mints; only the
|
|
225
|
+
* envelope differs, because an RFC-compliant client parses this one and knows
|
|
226
|
+
* nothing about Fleetless. So a consumer holding this **normalises it into
|
|
227
|
+
* `sessionTokens` and stores that** — it does not carry the envelope around.
|
|
228
|
+
* Written here rather than invented once in the cloud and once in the SDK,
|
|
229
|
+
* which is how two implementations of one wire shape start disagreeing.
|
|
230
|
+
*
|
|
231
|
+
* `token_type` is `Bearer` as a literal because it is what this server emits.
|
|
232
|
+
* RFC 6749 §5.1 makes the value case-insensitive **for a client reading it**;
|
|
233
|
+
* that leniency belongs in a parser we do not own, not in the shape we
|
|
234
|
+
* produce.
|
|
235
|
+
*/
|
|
236
|
+
export declare const oauthTokenResponse: z.ZodObject<{
|
|
237
|
+
access_token: z.ZodString;
|
|
238
|
+
token_type: z.ZodLiteral<"Bearer">;
|
|
239
|
+
expires_in: z.ZodNumber;
|
|
240
|
+
refresh_token: z.ZodOptional<z.ZodString>;
|
|
241
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
242
|
+
}, z.core.$strip>;
|
|
243
|
+
export type OauthTokenResponse = z.infer<typeof oauthTokenResponse>;
|
|
244
|
+
/** RFC 8414 §2 — the document a client reads *instead of* being told anything. */
|
|
245
|
+
export declare const authorizationServerMetadata: z.ZodObject<{
|
|
246
|
+
issuer: z.ZodURL;
|
|
247
|
+
authorization_endpoint: z.ZodURL;
|
|
248
|
+
token_endpoint: z.ZodURL;
|
|
249
|
+
registration_endpoint: z.ZodOptional<z.ZodURL>;
|
|
250
|
+
response_types_supported: z.ZodArray<z.ZodLiteral<"code">>;
|
|
251
|
+
grant_types_supported: z.ZodArray<z.ZodEnum<{
|
|
252
|
+
refresh_token: "refresh_token";
|
|
253
|
+
authorization_code: "authorization_code";
|
|
254
|
+
}>>;
|
|
255
|
+
code_challenge_methods_supported: z.ZodArray<z.ZodEnum<{
|
|
256
|
+
S256: "S256";
|
|
257
|
+
}>>;
|
|
258
|
+
token_endpoint_auth_methods_supported: z.ZodArray<z.ZodLiteral<"none">>;
|
|
259
|
+
scopes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
260
|
+
}, z.core.$strip>;
|
|
261
|
+
export type AuthorizationServerMetadata = z.infer<typeof authorizationServerMetadata>;
|
|
262
|
+
/**
|
|
263
|
+
* RFC 9728 — what a *resource* publishes about who may authorize for it.
|
|
264
|
+
*
|
|
265
|
+
* W7b mints tokens bound to a resource that W7c builds. **A minting mechanism
|
|
266
|
+
* with no validator is the failure mode this project has now met twelve times
|
|
267
|
+
* in one wave: a check that cannot fail.** So W7b also ships a resource that
|
|
268
|
+
* *rejects* a token whose audience names something else, and the gate measures
|
|
269
|
+
* the rejection rather than the presence of the claim.
|
|
270
|
+
*/
|
|
271
|
+
export declare const protectedResourceMetadata: z.ZodObject<{
|
|
272
|
+
resource: z.ZodURL;
|
|
273
|
+
authorization_servers: z.ZodArray<z.ZodURL>;
|
|
274
|
+
bearer_methods_supported: z.ZodArray<z.ZodLiteral<"header">>;
|
|
275
|
+
scopes_supported: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
276
|
+
}, z.core.$strip>;
|
|
277
|
+
export type ProtectedResourceMetadata = z.infer<typeof protectedResourceMetadata>;
|
|
278
|
+
/**
|
|
279
|
+
* Where the page goes next, and this shape is a **redirect the server chose**,
|
|
280
|
+
* never one the page may be talked into.
|
|
281
|
+
*
|
|
282
|
+
* The server emits exactly two kinds of value here: its own consent path, or a
|
|
283
|
+
* redirect URI already registered for this client with the code appended.
|
|
284
|
+
* A page must navigate to it and nothing else — in particular it must not fall
|
|
285
|
+
* back to any URL that arrived in its own query string if this field is
|
|
286
|
+
* missing, which is how an open redirect gets built by accident on the way to
|
|
287
|
+
* handling an error.
|
|
288
|
+
*
|
|
289
|
+
* JSON rather than a `302` because the page is an application: a redirect
|
|
290
|
+
* cannot carry a field-level credential error back to a form, and a flow that
|
|
291
|
+
* answers errors by navigating loses the state the user typed.
|
|
292
|
+
*/
|
|
293
|
+
export declare const oauthRedirectResponse: z.ZodObject<{
|
|
294
|
+
redirect_to: z.ZodString;
|
|
295
|
+
}, z.core.$strip>;
|
|
296
|
+
export type OauthRedirectResponse = z.infer<typeof oauthRedirectResponse>;
|
|
297
|
+
/**
|
|
298
|
+
* The authorization request of RFC 6749 §4.1.1 with PKCE (RFC 7636) — the
|
|
299
|
+
* shape `/mcp/oauth/authorize` reads.
|
|
300
|
+
*
|
|
301
|
+
* **The cloud reads every parameter by hand, and that is not an omission** —
|
|
302
|
+
* each failure has its own answer. `client_id` and `redirect_uri` are refused
|
|
303
|
+
* flat, with no redirect, because until both are confirmed there is no trusted
|
|
304
|
+
* target to bounce a browser to; everything after them is reported to the
|
|
305
|
+
* client's own callback as query parameters. A single `safeParse` would
|
|
306
|
+
* collapse those two answers into one. So this schema pins the successful
|
|
307
|
+
* shape and the documentation, not the error path.
|
|
308
|
+
*
|
|
309
|
+
* **Four route entries point at it**: `GET /mcp/oauth/authorize` and
|
|
310
|
+
* `GET /mcp/:appIdentifier/oauth/authorize` (D7), which read the same wire.
|
|
311
|
+
* They spent a release naming nothing — the app-level `/oauth/authorize` this
|
|
312
|
+
* was written for was deleted, and `query: null` was read as "there is no
|
|
313
|
+
* query here" rather than as "the handler reads it by hand" — and the eight
|
|
314
|
+
* documented parameters left `/openapi.json` with nothing able to notice,
|
|
315
|
+
* because the undocumented-field ratchet counts gaps and a fully documented
|
|
316
|
+
* schema leaving makes that number improve.
|
|
317
|
+
*/
|
|
318
|
+
export declare const oauthAuthorizeQuery: z.ZodObject<{
|
|
319
|
+
response_type: z.ZodLiteral<"code">;
|
|
320
|
+
client_id: z.ZodString;
|
|
321
|
+
redirect_uri: z.ZodString;
|
|
322
|
+
code_challenge: z.ZodString;
|
|
323
|
+
code_challenge_method: z.ZodLiteral<"S256">;
|
|
324
|
+
state: z.ZodOptional<z.ZodString>;
|
|
325
|
+
resource: z.ZodOptional<z.ZodString>;
|
|
326
|
+
}, z.core.$strip>;
|
|
327
|
+
export type OauthAuthorizeQuery = z.infer<typeof oauthAuthorizeQuery>;
|
|
328
|
+
/**
|
|
329
|
+
* **`oauthRegisterQuery` is deleted, and this note is what it leaves behind.**
|
|
330
|
+
*
|
|
331
|
+
* It carried one parameter, `app_identifier`, on the argument that RFC 7591's
|
|
332
|
+
* registration body has no field for it and one endpoint could serve every
|
|
333
|
+
* app. It was kept — explicitly, in its own doc comment — "for the per-app MCP
|
|
334
|
+
* registration the MCP train adds (D7), which needs exactly this parameter".
|
|
335
|
+
*
|
|
336
|
+
* **That train shipped and needed no such parameter.** `POST
|
|
337
|
+
* /mcp/:appIdentifier/oauth/register` puts the app in the **path**, built by
|
|
338
|
+
* `MCP_APP_PATHS`, and `resolveAppMcpTarget` reads it from `request.params`;
|
|
339
|
+
* `registerMcpDynamicClient` never looks at a query at all. So the one reason
|
|
340
|
+
* the schema was kept became false the moment its successor arrived, and
|
|
341
|
+
* nothing was watching the reason — which is the failure this comment is here
|
|
342
|
+
* to make expensive to repeat. A schema reserved for a future route is a claim
|
|
343
|
+
* with an expiry date on it, and the expiry has to be checked by somebody.
|
|
344
|
+
*/
|