@every-app/sdk 0.3.0-rc.8 → 0.3.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.
Files changed (146) hide show
  1. package/README.md +75 -74
  2. package/dist/cloudflare/catalogSettings.generated.d.ts +20 -6
  3. package/dist/cloudflare/catalogSettings.generated.d.ts.map +1 -1
  4. package/dist/cloudflare/catalogSettings.generated.js +45 -14
  5. package/dist/cloudflare/index.d.ts +1 -1
  6. package/dist/cloudflare/index.d.ts.map +1 -1
  7. package/dist/cloudflare/sentinel.d.ts +3 -0
  8. package/dist/cloudflare/sentinel.d.ts.map +1 -1
  9. package/dist/cloudflare/sentinel.js +10 -0
  10. package/dist/cloudflare/worker.d.ts +67 -23
  11. package/dist/cloudflare/worker.d.ts.map +1 -1
  12. package/dist/cloudflare/worker.js +68 -32
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/internal/aiDefault.d.ts +36 -79
  16. package/dist/internal/aiDefault.d.ts.map +1 -1
  17. package/dist/internal/aiDefault.js +47 -149
  18. package/dist/internal/aiProviders.d.ts +1 -1
  19. package/dist/internal/aiProviders.d.ts.map +1 -1
  20. package/dist/internal/aiProviders.js +2 -2
  21. package/dist/internal/appSettings.d.ts +140 -89
  22. package/dist/internal/appSettings.d.ts.map +1 -1
  23. package/dist/internal/appSettings.js +264 -156
  24. package/dist/internal/capabilities.d.ts +145 -503
  25. package/dist/internal/capabilities.d.ts.map +1 -1
  26. package/dist/internal/capabilities.js +29 -550
  27. package/dist/internal/catalog/commerce.js +1 -1
  28. package/dist/internal/catalog/comms.d.ts +37 -0
  29. package/dist/internal/catalog/comms.d.ts.map +1 -1
  30. package/dist/internal/catalog/comms.js +43 -0
  31. package/dist/internal/catalog/index.d.ts +72 -0
  32. package/dist/internal/catalog/index.d.ts.map +1 -1
  33. package/dist/internal/catalog/infra.d.ts +35 -0
  34. package/dist/internal/catalog/infra.d.ts.map +1 -1
  35. package/dist/internal/catalog/infra.js +65 -0
  36. package/dist/internal/devProviders.d.ts +1 -19
  37. package/dist/internal/devProviders.d.ts.map +1 -1
  38. package/dist/internal/devProviders.js +7 -1
  39. package/dist/internal/events.d.ts +52 -1
  40. package/dist/internal/events.d.ts.map +1 -1
  41. package/dist/internal/events.js +66 -2
  42. package/dist/internal/gatewaySettings.d.ts +6 -16
  43. package/dist/internal/gatewaySettings.d.ts.map +1 -1
  44. package/dist/internal/gatewaySettings.js +6 -24
  45. package/dist/internal/index.d.ts +1 -0
  46. package/dist/internal/index.d.ts.map +1 -1
  47. package/dist/internal/index.js +1 -0
  48. package/dist/internal/mcpOAuth.d.ts +210 -0
  49. package/dist/internal/mcpOAuth.d.ts.map +1 -0
  50. package/dist/internal/mcpOAuth.js +735 -0
  51. package/dist/internal/oauthClient.d.ts +7 -47
  52. package/dist/internal/oauthClient.d.ts.map +1 -1
  53. package/dist/internal/oauthClient.js +1 -115
  54. package/dist/internal/oauthPresets.d.ts +356 -20
  55. package/dist/internal/oauthPresets.d.ts.map +1 -1
  56. package/dist/internal/oauthPresets.js +183 -861
  57. package/dist/internal/oauthScopes.d.ts +43 -155
  58. package/dist/internal/oauthScopes.d.ts.map +1 -1
  59. package/dist/internal/oauthScopes.js +82 -475
  60. package/dist/internal/oauthSpec.d.ts +28 -71
  61. package/dist/internal/oauthSpec.d.ts.map +1 -1
  62. package/dist/internal/oauthSpec.js +172 -448
  63. package/dist/internal/protocol.d.ts +39 -15
  64. package/dist/internal/protocol.d.ts.map +1 -1
  65. package/dist/internal/protocol.js +40 -7
  66. package/dist/internal/providerApps.d.ts +2 -2
  67. package/dist/internal/providerApps.js +2 -2
  68. package/dist/internal/providerRegistry.d.ts +5 -64
  69. package/dist/internal/providerRegistry.d.ts.map +1 -1
  70. package/dist/internal/providerRegistry.js +19 -118
  71. package/dist/internal/providerSpec.d.ts.map +1 -1
  72. package/dist/internal/providerSpec.js +14 -36
  73. package/dist/internal/remoteMcp.d.ts +191 -47
  74. package/dist/internal/remoteMcp.d.ts.map +1 -1
  75. package/dist/internal/remoteMcp.js +338 -133
  76. package/dist/internal/signInProviders.d.ts +1 -1
  77. package/dist/internal/signInProviders.d.ts.map +1 -1
  78. package/dist/internal/tools.d.ts +15 -8
  79. package/dist/internal/tools.d.ts.map +1 -1
  80. package/dist/internal/tools.js +9 -8
  81. package/dist/manifest.d.ts +96 -28
  82. package/dist/manifest.d.ts.map +1 -1
  83. package/dist/manifest.js +1 -1
  84. package/dist/server/ai.d.ts +1 -93
  85. package/dist/server/ai.d.ts.map +1 -1
  86. package/dist/server/ai.js +17 -111
  87. package/dist/server/attribution.d.ts +10 -1
  88. package/dist/server/attribution.d.ts.map +1 -1
  89. package/dist/server/attribution.js +7 -1
  90. package/dist/server/connectUrl.d.ts +4 -9
  91. package/dist/server/connectUrl.d.ts.map +1 -1
  92. package/dist/server/connectUrl.js +17 -14
  93. package/dist/server/context.d.ts +12 -14
  94. package/dist/server/context.d.ts.map +1 -1
  95. package/dist/server/context.js +23 -25
  96. package/dist/server/emit.d.ts.map +1 -1
  97. package/dist/server/emit.js +2 -2
  98. package/dist/server/env.d.ts +3 -0
  99. package/dist/server/env.d.ts.map +1 -1
  100. package/dist/server/eventsEndpoint.d.ts +48 -0
  101. package/dist/server/eventsEndpoint.d.ts.map +1 -0
  102. package/dist/server/eventsEndpoint.js +145 -0
  103. package/dist/server/everyApp.d.ts +16 -26
  104. package/dist/server/everyApp.d.ts.map +1 -1
  105. package/dist/server/everyApp.js +22 -46
  106. package/dist/server/gatewayFetch.d.ts +19 -1
  107. package/dist/server/gatewayFetch.d.ts.map +1 -1
  108. package/dist/server/gatewayFetch.js +45 -3
  109. package/dist/server/index.d.ts +6 -9
  110. package/dist/server/index.d.ts.map +1 -1
  111. package/dist/server/index.js +4 -8
  112. package/dist/server/integrationFetch.d.ts +60 -72
  113. package/dist/server/integrationFetch.d.ts.map +1 -1
  114. package/dist/server/integrationFetch.js +124 -397
  115. package/dist/server/listMembers.d.ts.map +1 -1
  116. package/dist/server/listMembers.js +2 -2
  117. package/dist/server/refusal.d.ts +0 -9
  118. package/dist/server/refusal.d.ts.map +1 -1
  119. package/dist/server/refusal.js +0 -22
  120. package/dist/server/sendEmail.d.ts.map +1 -1
  121. package/dist/server/sendEmail.js +2 -2
  122. package/dist/server/tools.d.ts +17 -6
  123. package/dist/server/tools.d.ts.map +1 -1
  124. package/dist/server/tools.js +14 -4
  125. package/dist/server/toolsEndpoint.d.ts +2 -1
  126. package/dist/server/toolsEndpoint.d.ts.map +1 -1
  127. package/dist/server/toolsEndpoint.js +4 -3
  128. package/dist/server/webhooks.d.ts +56 -0
  129. package/dist/server/webhooks.d.ts.map +1 -0
  130. package/dist/server/webhooks.js +205 -0
  131. package/dist/testing/index.d.ts +2 -3
  132. package/dist/testing/index.d.ts.map +1 -1
  133. package/dist/testing/index.js +2 -6
  134. package/package.json +6 -1
  135. package/dist/server/aiBinding.d.ts +0 -68
  136. package/dist/server/aiBinding.d.ts.map +0 -1
  137. package/dist/server/aiBinding.js +0 -294
  138. package/dist/server/authorizeUrl.d.ts +0 -43
  139. package/dist/server/authorizeUrl.d.ts.map +0 -1
  140. package/dist/server/authorizeUrl.js +0 -106
  141. package/dist/server/emailBinding.d.ts +0 -72
  142. package/dist/server/emailBinding.d.ts.map +0 -1
  143. package/dist/server/emailBinding.js +0 -124
  144. package/dist/server/fromMcpTools.d.ts +0 -89
  145. package/dist/server/fromMcpTools.d.ts.map +0 -1
  146. package/dist/server/fromMcpTools.js +0 -192
@@ -1,36 +1,55 @@
1
1
  /**
2
2
  * Remote MCP apps: any remote MCP server registered on a gateway by URL
3
- * (`everyapp mcp add <url>`, Admin → Apps → Add app → "Add an MCP server",
3
+ * (`everyapp mcp add <url>`, Admin → Apps → "Add MCP server",
4
4
  * `everyapp_mcp_add`), served to members' AI clients as an MCP app of the
5
5
  * Gateway's own, like a built-in provider app (./providerApps.ts) but with
6
- * its tools read from the server and each call forwarded to it with a
7
- * credential the Gateway holds (docs/design/2026-10-mcp-by-url.md).
6
+ * its tools read from the server and each call forwarded to it
7
+ * (docs/design/2026-10-mcp-by-url.md, docs/design/2026-11-mcp-apps-auth.md).
8
8
  *
9
9
  * The registration record is the app row's manifest: `id`, `name`,
10
10
  * `description` and a `remote` block ({@link RemoteMcpBlock}): the one URL
11
- * the Gateway ever fetches, how it authenticates ({@link RemoteMcpAuth},
12
- * the credential always the NAME of a Gateway setting, never a value), how
13
- * a tool's risk is decided when the server's annotations don't say, the
14
- * operator's per-tool overrides, and a revision the writers bump so the
15
- * Gateway re-lists the tools on first use after a change.
11
+ * the Gateway ever fetches, its credential ({@link RemoteMcpAuth}: none, or
12
+ * a provider of the registry, whose key or OAuth connection the Gateway
13
+ * holds and binds to the provider's hosts), how a tool's risk is decided
14
+ * when the server's annotations don't say, the operator's per-tool
15
+ * overrides, and a revision the writers bump so the Gateway re-lists the
16
+ * tools on first use after a change. An app with a provider also lists it
17
+ * under `capabilities` ({@link remoteMcpAppManifest}), so the settings
18
+ * model treats its key or client as any app's.
19
+ *
20
+ * A credential the registrar gives at `mcp add` becomes a provider row of
21
+ * the app's own, `<appId>-mcp` ({@link remoteMcpProviderId}; the specs are
22
+ * built in ./mcpOAuth.ts): an API-key row for a static value
23
+ * (`<APPID>_MCP_TOKEN`), a discovered `oauth2-user` row for OAuth
24
+ * (`<APPID>_MCP_CLIENT_ID`, `_SECRET`).
16
25
  *
17
26
  * Everything here is pure and shared by the CLI, the perimeter's registry
18
27
  * rules, the Gateway (which serves the row and validates again at every
19
28
  * fetch) and the settings model (./appSettings.ts), so the rules cannot
20
- * drift: what the URL may be, what the credential may be called, how a
21
- * server's tool names become the ToolBus's, and what risk a tool gets.
29
+ * drift: what the URL may be, what the credential may be, how a server's
30
+ * tool names become the ToolBus's, and what risk a tool gets.
22
31
  */
23
- import { CAPABILITIES } from "./capabilities.js";
24
- import { isRemoteMcpSecretName } from "./gatewaySettings.js";
32
+ import { CAPABILITIES, apiKeyHosts, } from "./capabilities.js";
25
33
  import { isProviderAppId } from "./providerApps.js";
26
- import { TOOL_NAME_PATTERN, isToolRisk } from "./tools.js";
27
- export const REMOTE_MCP_AUTH_KINDS = [
34
+ import { PROVIDER_ID } from "./providerSpec.js";
35
+ import { TOOL_NAME_PATTERN, canonicalJson, isToolRisk, } from "./tools.js";
36
+ /** The kinds a stored `remote.auth` block may have. */
37
+ export const REMOTE_MCP_AUTH_KINDS = ["none", "provider"];
38
+ /**
39
+ * What the registrar may ask for at `mcp add` (`--auth`): none, a static
40
+ * credential the add turns into a synthesized API-key provider row, or
41
+ * OAuth, a discovered `oauth2-user` row (./mcpOAuth.ts).
42
+ */
43
+ export const REMOTE_MCP_CREDENTIAL_KINDS = [
28
44
  "none",
29
45
  "bearer",
30
46
  "header",
47
+ "query",
31
48
  "basic",
32
49
  "oauth",
33
50
  ];
51
+ /** Why an old registration (`bearer`, `header`, `basic`, `oauth` with a `secret`) no longer parses. */
52
+ export const REMOTE_MCP_AUTH_PREDATES = "this registration predates provider credentials; remove it and add it again";
34
53
  /**
35
54
  * How a tool's risk is decided for the app. `annotations`: from the
36
55
  * server's hints ({@link remoteToolRisk}); `read` or `write`: every tool,
@@ -45,18 +64,17 @@ const ID_PATTERN = /^[a-z]([a-z0-9-]*[a-z0-9])?$/;
45
64
  const ID_MAX_LENGTH = 32;
46
65
  /** App ids a remote server can't take: the Gateway's own tool prefix. */
47
66
  const RESERVED_IDS = ["everyapp"];
48
- const SECRET_MAX_LENGTH = 64;
49
- const HEADER_NAME_PATTERN = /^[A-Za-z0-9!#$%&'*+.^_`|~-]{1,100}$/;
50
- const PREFIX_MAX_LENGTH = 32;
67
+ const HEADER_NAME_PATTERN = /^[A-Za-z0-9!#$%&'*+.^_`|~-]{1,64}$/;
51
68
  const URL_MAX_LENGTH = 2048;
52
69
  const NAME_MAX_LENGTH = 255;
53
70
  const DESCRIPTION_MAX_LENGTH = 1000;
54
71
  const OVERRIDES_MAX = 500;
55
72
  const UPSTREAM_NAME_MAX = 256;
56
73
  /**
57
- * Headers the `header` kind may not name: the ones the Gateway sets on the
58
- * request, the ones that route it, and the ones that would carry something
59
- * other than this app's credential.
74
+ * Headers a static `header` credential may not name: the ones the Gateway
75
+ * sets on the request, the ones that route it, and the ones that would
76
+ * carry something other than this app's credential (`Authorization` is
77
+ * the `bearer` kind's; `--prefix` gives it another scheme).
60
78
  */
61
79
  const FORBIDDEN_AUTH_HEADERS = new Set([
62
80
  "authorization",
@@ -71,10 +89,99 @@ const FORBIDDEN_AUTH_HEADERS = new Set([
71
89
  "mcp-protocol-version",
72
90
  "mcp-method",
73
91
  "mcp-name",
92
+ "last-event-id",
74
93
  ]);
75
- /** The default credential name for an app id: `treg` → `TREG_MCP_TOKEN`. */
76
- export function remoteMcpSecretName(id) {
77
- return `${id.toUpperCase().replace(/-/g, "_")}_MCP_TOKEN`;
94
+ /**
95
+ * Why `header` can't carry a remote MCP server's static credential, or
96
+ * null when it can: an HTTP header name the MCP transport and the Gateway
97
+ * don't set themselves.
98
+ */
99
+ export function remoteMcpHeaderProblem(header) {
100
+ if (typeof header !== "string" || !HEADER_NAME_PATTERN.test(header)) {
101
+ return "the header must be an HTTP header name";
102
+ }
103
+ if (FORBIDDEN_AUTH_HEADERS.has(header.toLowerCase())) {
104
+ return `the header can't be ${header}; use --auth bearer for Authorization`;
105
+ }
106
+ return null;
107
+ }
108
+ /** The settings prefix of an app id's synthesized row: `my-server` → `MY_SERVER_MCP_`. */
109
+ function settingsPrefix(appId) {
110
+ return `${appId.toUpperCase().replace(/-/g, "_")}_MCP_`;
111
+ }
112
+ /**
113
+ * The provider row `everyapp mcp add` writes for an app's own credential:
114
+ * `<appId>-mcp` (`treg` → `treg-mcp`), its settings `TREG_MCP_*`.
115
+ */
116
+ export function remoteMcpProviderId(appId) {
117
+ return `${appId}-mcp`;
118
+ }
119
+ /** Whether `providerId` is `appId`'s synthesized row ({@link remoteMcpProviderId}). */
120
+ export function isRemoteMcpProviderIdOf(providerId, appId) {
121
+ return providerId === remoteMcpProviderId(appId);
122
+ }
123
+ /** The static credential's setting on the synthesized row: `treg` → `TREG_MCP_TOKEN`. */
124
+ export function remoteMcpTokenSetting(appId) {
125
+ return `${settingsPrefix(appId)}TOKEN`;
126
+ }
127
+ /** The OAuth client id's setting on the synthesized row: `treg` → `TREG_MCP_CLIENT_ID`. */
128
+ export function remoteMcpClientIdSetting(appId) {
129
+ return `${settingsPrefix(appId)}CLIENT_ID`;
130
+ }
131
+ /** The OAuth client secret's setting on the synthesized row: `treg` → `TREG_MCP_CLIENT_SECRET`. */
132
+ export function remoteMcpClientSecretSetting(appId) {
133
+ return `${settingsPrefix(appId)}CLIENT_SECRET`;
134
+ }
135
+ /**
136
+ * The redirect URI of an app's synthesized OAuth row, what a server's
137
+ * client is registered with: `<gateway origin>/connect/<appId>-mcp/callback`.
138
+ */
139
+ export function remoteMcpRedirectUri(gatewayUrl, appId) {
140
+ let origin = gatewayUrl.trim().replace(/\/+$/, "");
141
+ try {
142
+ origin = new URL(gatewayUrl).origin;
143
+ }
144
+ catch {
145
+ // Not a URL: used as given, without a trailing slash.
146
+ }
147
+ return `${origin}/connect/${remoteMcpProviderId(appId)}/callback`;
148
+ }
149
+ /** Where the Gateway serves its OAuth client metadata documents (public, bypassed behind Access). */
150
+ export const REMOTE_MCP_CLIENT_METADATA_PREFIX = "/oauth-clients/";
151
+ /**
152
+ * The Gateway's client id at a server that takes OAuth client ID metadata
153
+ * documents (CIMD) instead of registration: the URL of the document that
154
+ * describes the client, `<gateway origin>/oauth-clients/<appId>-mcp`.
155
+ * Null for a Gateway that isn't on https (a local one): an authorization
156
+ * server fetches the document, and only from https.
157
+ */
158
+ export function remoteMcpClientMetadataUrl(gatewayUrl, appId) {
159
+ let origin;
160
+ try {
161
+ const parsed = new URL(gatewayUrl);
162
+ if (parsed.protocol !== "https:")
163
+ return null;
164
+ origin = parsed.origin;
165
+ }
166
+ catch {
167
+ return null;
168
+ }
169
+ return `${origin}${REMOTE_MCP_CLIENT_METADATA_PREFIX}${remoteMcpProviderId(appId)}`;
170
+ }
171
+ /**
172
+ * The hosts a provider sends its credential to, the hosts an MCP URL
173
+ * using it must be on: an API-key provider's `hosts` (and its host
174
+ * setting's value, given `settings`), an OAuth provider's API hosts. Null
175
+ * for a provider a remote MCP app can't use: an AI provider, a service,
176
+ * or an OAuth client the Gateway runs no flow for.
177
+ */
178
+ export function remoteMcpProviderHosts(capability, settings) {
179
+ if (capability.mode === "api_key")
180
+ return apiKeyHosts(capability, settings);
181
+ if (capability.mode === "oauth_client" && capability.flow) {
182
+ return [...capability.api];
183
+ }
184
+ return null;
78
185
  }
79
186
  /** Whether `id` can be a remote MCP app's id (kebab-case, at most 32, not reserved, not a built-in's). */
80
187
  export function remoteMcpIdProblem(id) {
@@ -140,19 +247,16 @@ export function reservedUpstreamHosts() {
140
247
  "openrouter.ai",
141
248
  "gateway.ai.cloudflare.com",
142
249
  "api.cloudflare.com",
250
+ // The token endpoints of the Google, Microsoft and Slack presets.
251
+ "oauth2.googleapis.com",
252
+ "login.microsoftonline.com",
253
+ "slack.com",
143
254
  ]);
144
255
  for (const capability of Object.values(CAPABILITIES)) {
145
256
  if (capability.mode === "api_key") {
146
257
  for (const host of capability.hosts)
147
258
  hosts.add(host);
148
259
  }
149
- else if (capability.mode === "oauth_client") {
150
- for (const endpoint of capability.token) {
151
- if ("host" in endpoint && typeof endpoint.host === "string") {
152
- hosts.add(endpoint.host);
153
- }
154
- }
155
- }
156
260
  }
157
261
  return [...hosts].sort();
158
262
  }
@@ -161,11 +265,16 @@ export function reservedUpstreamHosts() {
161
265
  * `https:` (or `http:` and loopback hosts only while the Gateway itself
162
266
  * runs on `http://`, local dev), a host name rather than an address, no
163
267
  * credentials or fragment in it, not the Gateway's own origin or one of
164
- * its app hosts, and not a provider whose key the Gateway holds. The
165
- * Gateway checks the stored URL again before every fetch, so a row edited
166
- * by hand is refused the same way.
268
+ * its app hosts, and not a provider whose key the Gateway holds
269
+ * ({@link reservedUpstreamHosts}) unless the app names that provider:
270
+ * `providerHosts` are the hosts of the provider the app's credential is
271
+ * (never an app's own synthesized row), and a reserved host among them is
272
+ * allowed (`mcp add https://api.elevenlabs.io/v1/mcp --provider
273
+ * elevenlabs`). The Gateway's own origin, its app domains and IPs stay
274
+ * refused always. The Gateway checks the stored URL again before every
275
+ * fetch, so a row edited by hand is refused the same way.
167
276
  */
168
- export function remoteMcpUrlProblem(url, env) {
277
+ export function remoteMcpUrlProblem(url, env, options = {}) {
169
278
  if (typeof url !== "string" || !url.trim())
170
279
  return "give the server's URL";
171
280
  if (url.length > URL_MAX_LENGTH)
@@ -234,74 +343,67 @@ export function remoteMcpUrlProblem(url, env) {
234
343
  return `the URL is on this Gateway's app domain (${normalized})`;
235
344
  }
236
345
  }
237
- if (reservedUpstreamHosts().includes(host)) {
238
- return `${host} is a provider this Gateway holds a key for; apps reach it through the Gateway's capabilities, not as an MCP app`;
346
+ const named = (options.providerHosts ?? []).map((each) => each.toLowerCase().replace(/\.$/, ""));
347
+ if (reservedUpstreamHosts().includes(host) && !named.includes(host)) {
348
+ return `${host} is a provider this Gateway holds a key for; add it with --provider <that provider> so the app uses its key, or reach it through the Gateway's capabilities`;
239
349
  }
240
350
  return null;
241
351
  }
242
352
  function isRecord(value) {
243
353
  return typeof value === "object" && value !== null && !Array.isArray(value);
244
354
  }
245
- /** The credential name's problem, or null: in the family, and bounded. */
246
- export function remoteMcpSecretProblem(name) {
247
- if (typeof name !== "string" || !name)
248
- return "give the credential's setting name";
249
- if (name.length > SECRET_MAX_LENGTH)
250
- return "the credential's name is too long";
251
- if (!isRemoteMcpSecretName(name)) {
252
- return `the credential's name must end in _MCP_TOKEN (letters, digits and underscores), like ${remoteMcpSecretName("treg")}`;
253
- }
254
- return null;
255
- }
256
- /** The auth block's problem, or the block as typed. */
355
+ /**
356
+ * The auth block's problem, or the block as typed: `{ kind: "none" }`,
357
+ * or `{ kind: "provider", provider }` naming a provider id. A
358
+ * block of the kinds before provider credentials (`bearer`, `header`,
359
+ * `basic`, `oauth`) is refused with {@link REMOTE_MCP_AUTH_PREDATES}: such
360
+ * a row serves nothing and reads as needing setup.
361
+ */
257
362
  export function parseRemoteMcpAuth(raw) {
258
363
  if (!isRecord(raw) || typeof raw["kind"] !== "string") {
259
364
  return { ok: false, problem: "auth must name a kind" };
260
365
  }
261
366
  const kind = raw["kind"];
262
- if (!REMOTE_MCP_AUTH_KINDS.includes(kind)) {
367
+ if (["bearer", "header", "basic", "oauth"].includes(kind)) {
368
+ return { ok: false, problem: REMOTE_MCP_AUTH_PREDATES };
369
+ }
370
+ if (kind === "none") {
371
+ for (const field of Object.keys(raw)) {
372
+ if (field !== "kind") {
373
+ return {
374
+ ok: false,
375
+ problem: `auth.${field} is not a field of kind none`,
376
+ };
377
+ }
378
+ }
379
+ return { ok: true, auth: { kind } };
380
+ }
381
+ if (kind !== "provider") {
263
382
  return {
264
383
  ok: false,
265
384
  problem: `auth.kind must be one of ${REMOTE_MCP_AUTH_KINDS.join(", ")}`,
266
385
  };
267
386
  }
268
- if (kind === "none" || kind === "oauth") {
269
- return { ok: true, auth: { kind } };
270
- }
271
- const secretProblem = remoteMcpSecretProblem(raw["secret"]);
272
- if (secretProblem)
273
- return { ok: false, problem: `auth.secret: ${secretProblem}` };
274
- const secret = raw["secret"];
275
- if (kind === "bearer" || kind === "basic") {
276
- return { ok: true, auth: { kind, secret } };
277
- }
278
- const header = raw["header"];
279
- if (typeof header !== "string" || !HEADER_NAME_PATTERN.test(header)) {
280
- return { ok: false, problem: "auth.header must be an HTTP header name" };
387
+ for (const field of Object.keys(raw)) {
388
+ if (!["kind", "provider"].includes(field)) {
389
+ return {
390
+ ok: false,
391
+ problem: `auth.${field} is not a field of kind provider`,
392
+ };
393
+ }
281
394
  }
282
- if (FORBIDDEN_AUTH_HEADERS.has(header.toLowerCase())) {
395
+ const provider = raw["provider"];
396
+ if (typeof provider !== "string" || !PROVIDER_ID.test(provider)) {
283
397
  return {
284
398
  ok: false,
285
- problem: `auth.header can't be ${header}; use kind "bearer" or "basic" for Authorization`,
399
+ problem: "auth.provider must be a provider id: lowercase letters, digits and hyphens, a letter first",
286
400
  };
287
401
  }
288
- const prefix = raw["prefix"];
289
- if (prefix !== undefined) {
290
- if (typeof prefix !== "string" ||
291
- prefix.length > PREFIX_MAX_LENGTH ||
292
- /[\r\n]/.test(prefix)) {
293
- return { ok: false, problem: "auth.prefix must be a short string" };
294
- }
295
- }
296
- return {
297
- ok: true,
298
- auth: {
299
- kind: "header",
300
- secret,
301
- header,
302
- ...(typeof prefix === "string" && prefix ? { prefix } : {}),
303
- },
304
- };
402
+ return { ok: true, auth: { kind: "provider", provider } };
403
+ }
404
+ /** The provider the block names, or null for `none`. */
405
+ export function remoteMcpProviderOf(auth) {
406
+ return auth.kind === "provider" ? auth.provider : null;
305
407
  }
306
408
  /**
307
409
  * The `remote` block of a stored manifest, checked field by field (a row
@@ -352,7 +454,7 @@ export function parseRemoteMcpBlock(raw) {
352
454
  if (!isToolRisk(override["risk"])) {
353
455
  return {
354
456
  ok: false,
355
- problem: `remote.tools.${name}.risk must be read, write or destructive`,
457
+ problem: `remote.tools.${name}.risk must be read or write`,
356
458
  };
357
459
  }
358
460
  out.risk = override["risk"];
@@ -395,14 +497,21 @@ export function remoteMcpOf(manifest) {
395
497
  const parsed = parseRemoteMcpBlock(manifest["remote"]);
396
498
  return parsed.ok ? parsed.block : null;
397
499
  }
500
+ /**
501
+ * Why a stored manifest's `remote` block can't be read, or null when it
502
+ * can or there is none: {@link REMOTE_MCP_AUTH_PREDATES} for a block from
503
+ * before provider credentials.
504
+ */
505
+ export function remoteMcpBlockProblem(manifest) {
506
+ if (!isRecord(manifest) || manifest["remote"] === undefined)
507
+ return null;
508
+ const parsed = parseRemoteMcpBlock(manifest["remote"]);
509
+ return parsed.ok ? null : parsed.problem;
510
+ }
398
511
  /** Whether a stored manifest declares a remote MCP server at all (well-formed or not). */
399
512
  export function hasRemoteMcpBlock(manifest) {
400
513
  return isRecord(manifest) && manifest["remote"] !== undefined;
401
514
  }
402
- /** The credential's setting name the block declares, if its kind takes one. */
403
- export function remoteMcpSecretOf(auth) {
404
- return "secret" in auth ? auth.secret : null;
405
- }
406
515
  /**
407
516
  * A description for a row that gave none: the server's origin, read as a
408
517
  * sentence. The origin only: a path or query may carry something the
@@ -419,6 +528,77 @@ export function remoteMcpDefaultDescription(name, url) {
419
528
  }
420
529
  return `${name}'s MCP server${where ? ` at ${where}` : ""}, served to your AI clients through this Gateway.`;
421
530
  }
531
+ /** One line of a server's own text: no control characters, spaces collapsed, bounded. */
532
+ function serverText(value, max) {
533
+ if (typeof value !== "string")
534
+ return "";
535
+ return value
536
+ .replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]+/g, " ")
537
+ .replace(/\s+/g, " ")
538
+ .trim()
539
+ .slice(0, max)
540
+ .trim();
541
+ }
542
+ /**
543
+ * The name and description a row takes from its server's `serverInfo`, or
544
+ * null when nothing changes. A row is named by the server unless the
545
+ * registrar named it: a name that is still the app id (none given) or the
546
+ * server's own last title gives way to the server's `title` (or a `name`
547
+ * that reads as one, not a code identifier like `mcp-server`); a
548
+ * description that is still the default sentence or the server's last one
549
+ * gives way to the server's `description`. What the server said last is
550
+ * kept in the manifest's `server`, so a later change on the server follows
551
+ * and a name or description the registrar set (Edit in Admin → Apps) is never
552
+ * overwritten.
553
+ */
554
+ export function remoteMcpServerNaming(input) {
555
+ let stored;
556
+ try {
557
+ const parsed = JSON.parse(input.manifest ?? "");
558
+ if (!isRecord(parsed))
559
+ return null;
560
+ stored = parsed;
561
+ }
562
+ catch {
563
+ return null;
564
+ }
565
+ const name = typeof stored["name"] === "string" ? stored["name"] : input.id;
566
+ const description = typeof stored["description"] === "string" ? stored["description"] : "";
567
+ const last = isRecord(stored["server"]) ? stored["server"] : {};
568
+ const info = input.serverInfo ?? {};
569
+ const rawName = serverText(info.name, NAME_MAX_LENGTH);
570
+ const serverName = serverText(info.title, NAME_MAX_LENGTH) ||
571
+ (/[A-Z\s]/.test(rawName) ? rawName : "");
572
+ const serverDescription = serverText(info.description, DESCRIPTION_MAX_LENGTH);
573
+ const nameIsServers = name === input.id ||
574
+ (last["title"] !== undefined && name === last["title"]);
575
+ const nextName = nameIsServers ? serverName || input.id : name;
576
+ const descriptionIsServers = !description ||
577
+ description === remoteMcpDefaultDescription(name, input.url) ||
578
+ (last["description"] !== undefined && description === last["description"]);
579
+ const nextDescription = descriptionIsServers
580
+ ? serverDescription || remoteMcpDefaultDescription(nextName, input.url)
581
+ : description;
582
+ const server = {
583
+ ...(serverName ? { title: serverName } : {}),
584
+ ...(serverDescription ? { description: serverDescription } : {}),
585
+ };
586
+ if (nextName === name &&
587
+ nextDescription === description &&
588
+ canonicalJson(server) === canonicalJson(last)) {
589
+ return null;
590
+ }
591
+ return {
592
+ manifest: JSON.stringify({
593
+ ...stored,
594
+ name: nextName,
595
+ description: nextDescription,
596
+ server,
597
+ }),
598
+ name: nextName,
599
+ description: nextDescription,
600
+ };
601
+ }
422
602
  /**
423
603
  * The operator's override for a server tool: by the server's name, or by
424
604
  * a name that becomes the same ToolBus name ({@link normalizeRemoteToolName}),
@@ -442,21 +622,52 @@ export function remoteToolOverride(block, upstreamName) {
442
622
  export function isHeaderSafeToolName(name) {
443
623
  return /^[\x21-\x7e]{1,256}$/.test(name);
444
624
  }
445
- /** The manifest a remote MCP app's row stores, from what the registrar gave. */
625
+ /**
626
+ * The manifest a remote MCP app's row stores, from what the registrar
627
+ * gave. An app with a provider credential lists the provider under
628
+ * `capabilities` like a deployed app that uses it, so the settings model
629
+ * (./appSettings.ts) asks for its key or client, `secret list` names it
630
+ * and the connect flow asks for its scopes: nothing about a remote app's
631
+ * credential is special there. `providerCapability` is the provider as
632
+ * the registry resolves it (the synthesized row's, before it is written);
633
+ * for an OAuth provider the entry lists `scopes`, the server's own scope
634
+ * strings the connect asks for (discovery's, or none: the server decides).
635
+ */
446
636
  export function remoteMcpAppManifest(input) {
447
637
  const name = (input.name ?? "").trim().slice(0, NAME_MAX_LENGTH) || input.id;
448
638
  const description = (input.description ?? "").trim().slice(0, DESCRIPTION_MAX_LENGTH) ||
449
639
  remoteMcpDefaultDescription(name, input.remote.url);
450
- return { id: input.id, name, description, remote: input.remote };
640
+ const manifest = {
641
+ id: input.id,
642
+ name,
643
+ description,
644
+ remote: input.remote,
645
+ };
646
+ const auth = input.remote.auth;
647
+ if (auth.kind !== "provider")
648
+ return manifest;
649
+ let where = "";
650
+ try {
651
+ where = new URL(input.remote.url).origin;
652
+ }
653
+ catch {
654
+ where = "the server";
655
+ }
656
+ const why = `${name}'s MCP server at ${where} takes this credential; the Gateway sends it there when a member calls the app's tools.`;
657
+ const shortWhy = why.length > 200 ? `${why.slice(0, 199)}…` : why;
658
+ manifest.capabilities = {
659
+ [auth.provider]: input.providerCapability?.mode === "oauth_client"
660
+ ? { scopes: [...(input.scopes ?? [])], why: shortWhy }
661
+ : { access: "read", why: shortWhy },
662
+ };
663
+ return manifest;
451
664
  }
452
665
  /**
453
666
  * A tool's risk for the Gateway. An override wins; then the app's `read`
454
- * or `write` default; then the server's hints, read conservatively: an
455
- * explicit `readOnlyHint: true` is `read`, an explicit `destructiveHint:
456
- * true` is `destructive` (hidden until approvals exist), and anything
457
- * else, a missing hint included, is `write`. The spec's missing-means-true
458
- * default for `destructiveHint` is not followed: it would hide every
459
- * unhinted tool.
667
+ * or `write` default; then the server's hints: an explicit `readOnlyHint:
668
+ * true` is `read`, and anything else, a missing hint included, is `write`.
669
+ * A write tool keeps the server's `destructiveHint`, so clients still ask
670
+ * before calling it.
460
671
  */
461
672
  export function remoteToolRisk(hints, block, upstreamName) {
462
673
  const override = remoteToolOverride(block, upstreamName)?.risk;
@@ -467,9 +678,8 @@ export function remoteToolRisk(hints, block, upstreamName) {
467
678
  }
468
679
  if (hints?.readOnlyHint === true)
469
680
  return { risk: "read", source: "hint" };
470
- if (hints?.destructiveHint === true) {
471
- return { risk: "destructive", source: "hint" };
472
- }
681
+ if (hints?.destructiveHint === true)
682
+ return { risk: "write", source: "hint" };
473
683
  return { risk: "write", source: "default" };
474
684
  }
475
685
  /** Whether the operator left the tool on (the default). */
@@ -503,41 +713,36 @@ export function normalizeRemoteToolName(name) {
503
713
  return TOOL_NAME_PATTERN.test(out) ? out : null;
504
714
  }
505
715
  /**
506
- * The request headers that carry the credential for `auth`, from the
507
- * setting's value. The value never appears anywhere else.
716
+ * Plain words for how the app authenticates, for lists and dialogs. With
717
+ * the provider's capability as the registry resolves it: "bearer token",
718
+ * "header X-Api-Key", "query parameter key", "user and password" (an
719
+ * API-key provider; "<kind>, <provider>'s key" when the provider isn't
720
+ * the app's own synthesized row), or "OAuth, each member connects" (an
721
+ * OAuth provider). Without it, the provider's id.
508
722
  */
509
- export function remoteMcpAuthHeaders(auth, value) {
510
- switch (auth.kind) {
511
- case "none":
512
- case "oauth":
513
- return {};
514
- case "bearer":
515
- return { authorization: `Bearer ${value}` };
516
- case "basic":
517
- return { authorization: `Basic ${base64(value)}` };
518
- case "header":
519
- return { [auth.header]: `${auth.prefix ?? ""}${value}` };
520
- }
521
- }
522
- function base64(text) {
523
- const bytes = new TextEncoder().encode(text);
524
- let binary = "";
525
- for (const byte of bytes)
526
- binary += String.fromCharCode(byte);
527
- return btoa(binary);
528
- }
529
- /** Plain words for an auth kind, for lists and dialogs. */
530
- export function remoteMcpAuthLabel(auth) {
531
- switch (auth.kind) {
532
- case "none":
533
- return "no credential";
534
- case "bearer":
535
- return "bearer token";
536
- case "basic":
537
- return "user and password";
538
- case "header":
539
- return `header ${auth.header}`;
540
- case "oauth":
541
- return "OAuth (not supported yet)";
542
- }
723
+ export function remoteMcpAuthLabel(auth, capability) {
724
+ if (auth.kind === "none")
725
+ return "no credential";
726
+ if (!capability)
727
+ return `provider ${auth.provider}`;
728
+ if (capability.mode === "oauth_client") {
729
+ return "OAuth, each member connects";
730
+ }
731
+ if (capability.mode !== "api_key")
732
+ return `provider ${auth.provider}`;
733
+ const placement = capability.auth;
734
+ const words = placement.kind === "bearer"
735
+ ? placement.scheme && placement.scheme.toLowerCase() !== "bearer"
736
+ ? `${placement.scheme} token`
737
+ : "bearer token"
738
+ : placement.kind === "header"
739
+ ? `header ${placement.header}`
740
+ : placement.kind === "query"
741
+ ? `query parameter ${placement.param}`
742
+ : placement.as === "pair"
743
+ ? "user and password"
744
+ : "basic auth";
745
+ return auth.provider.endsWith("-mcp")
746
+ ? words
747
+ : `${words}, ${capability.label}'s key`;
543
748
  }
@@ -54,7 +54,7 @@ export interface SignInProvider {
54
54
  readonly scopes: readonly string[];
55
55
  /** Which claim becomes the email, and what makes it count as verified. */
56
56
  readonly emailClaim: string;
57
- /** The everyapp skill's setup guide, relative to the skill. */
57
+ /** The setup guide, relative to the agent docs (https://everyapp.com/docs/agent/). */
58
58
  readonly setup: `setup/signin-${SignInProviderId}.md`;
59
59
  /** Caveats worth a line in every listing. */
60
60
  readonly notes: readonly string[];
@@ -1 +1 @@
1
- {"version":3,"file":"signInProviders.d.ts","sourceRoot":"","sources":["../../src/internal/signInProviders.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"signInProviders.d.ts","sourceRoot":"","sources":["../../src/internal/signInProviders.ts"],"names":[],"mappings":"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"}