@every-app/sdk 0.2.0 → 0.3.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +608 -2
- package/dist/cloudflare/index.d.ts +22 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +21 -0
- package/dist/cloudflare/sentinel.d.ts +69 -0
- package/dist/cloudflare/sentinel.d.ts.map +1 -0
- package/dist/cloudflare/sentinel.js +50 -0
- package/dist/cloudflare/worker.d.ts +592 -0
- package/dist/cloudflare/worker.d.ts.map +1 -0
- package/dist/cloudflare/worker.js +817 -0
- package/dist/dev/index.d.ts +8 -0
- package/dist/dev/index.d.ts.map +1 -0
- package/dist/dev/index.js +7 -0
- package/dist/dev/keypair.d.ts +24 -0
- package/dist/dev/keypair.d.ts.map +1 -0
- package/dist/dev/keypair.js +60 -0
- package/dist/internal/aiProviders.d.ts +67 -0
- package/dist/internal/aiProviders.d.ts.map +1 -0
- package/dist/internal/aiProviders.js +220 -0
- package/dist/internal/appSettings.d.ts +237 -0
- package/dist/internal/appSettings.d.ts.map +1 -0
- package/dist/internal/appSettings.js +374 -0
- package/dist/internal/capabilities.d.ts +3249 -0
- package/dist/internal/capabilities.d.ts.map +1 -0
- package/dist/internal/capabilities.js +1532 -0
- package/dist/internal/catalog/commerce.d.ts +394 -0
- package/dist/internal/catalog/commerce.d.ts.map +1 -0
- package/dist/internal/catalog/commerce.js +477 -0
- package/dist/internal/catalog/comms.d.ts +338 -0
- package/dist/internal/catalog/comms.d.ts.map +1 -0
- package/dist/internal/catalog/comms.js +477 -0
- package/dist/internal/catalog/index.d.ts +1899 -0
- package/dist/internal/catalog/index.d.ts.map +1 -0
- package/dist/internal/catalog/index.js +19 -0
- package/dist/internal/catalog/inference.d.ts +356 -0
- package/dist/internal/catalog/inference.d.ts.map +1 -0
- package/dist/internal/catalog/inference.js +425 -0
- package/dist/internal/catalog/infra.d.ts +423 -0
- package/dist/internal/catalog/infra.d.ts.map +1 -0
- package/dist/internal/catalog/infra.js +543 -0
- package/dist/internal/catalog/search.d.ts +400 -0
- package/dist/internal/catalog/search.d.ts.map +1 -0
- package/dist/internal/catalog/search.js +463 -0
- package/dist/internal/devProviders.d.ts +49 -0
- package/dist/internal/devProviders.d.ts.map +1 -0
- package/dist/internal/devProviders.js +62 -0
- package/dist/internal/email.d.ts +280 -0
- package/dist/internal/email.d.ts.map +1 -0
- package/dist/internal/email.js +613 -0
- package/dist/internal/events.d.ts +182 -0
- package/dist/internal/events.d.ts.map +1 -0
- package/dist/internal/events.js +326 -0
- package/dist/internal/gatewaySettings.d.ts +83 -0
- package/dist/internal/gatewaySettings.d.ts.map +1 -0
- package/dist/internal/gatewaySettings.js +217 -0
- package/dist/internal/identity.d.ts +18 -3
- package/dist/internal/identity.d.ts.map +1 -1
- package/dist/internal/identity.js +53 -14
- package/dist/internal/index.d.ts +18 -1
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +18 -1
- package/dist/internal/members.d.ts +35 -0
- package/dist/internal/members.d.ts.map +1 -0
- package/dist/internal/members.js +7 -0
- package/dist/internal/oauthClient.d.ts +63 -0
- package/dist/internal/oauthClient.d.ts.map +1 -0
- package/dist/internal/oauthClient.js +194 -0
- package/dist/internal/oauthScopes.d.ts +117 -0
- package/dist/internal/oauthScopes.d.ts.map +1 -0
- package/dist/internal/oauthScopes.js +416 -0
- package/dist/internal/organizationName.d.ts +18 -0
- package/dist/internal/organizationName.d.ts.map +1 -0
- package/dist/internal/organizationName.js +24 -0
- package/dist/internal/protocol.d.ts +86 -2
- package/dist/internal/protocol.d.ts.map +1 -1
- package/dist/internal/protocol.js +112 -1
- package/dist/internal/providerApps.d.ts +75 -0
- package/dist/internal/providerApps.d.ts.map +1 -0
- package/dist/internal/providerApps.js +59 -0
- package/dist/internal/providerRegistry.d.ts +147 -0
- package/dist/internal/providerRegistry.d.ts.map +1 -0
- package/dist/internal/providerRegistry.js +266 -0
- package/dist/internal/providerSpec.d.ts +55 -0
- package/dist/internal/providerSpec.d.ts.map +1 -0
- package/dist/internal/providerSpec.js +758 -0
- package/dist/internal/remoteMcp.d.ts +188 -0
- package/dist/internal/remoteMcp.d.ts.map +1 -0
- package/dist/internal/remoteMcp.js +543 -0
- package/dist/internal/signInProviders.d.ts +104 -0
- package/dist/internal/signInProviders.d.ts.map +1 -0
- package/dist/internal/signInProviders.js +238 -0
- package/dist/internal/tools.d.ts +115 -0
- package/dist/internal/tools.d.ts.map +1 -0
- package/dist/internal/tools.js +80 -0
- package/dist/server/attribution.d.ts +39 -0
- package/dist/server/attribution.d.ts.map +1 -0
- package/dist/server/attribution.js +67 -0
- package/dist/server/authorizeUrl.d.ts +43 -0
- package/dist/server/authorizeUrl.d.ts.map +1 -0
- package/dist/server/authorizeUrl.js +110 -0
- package/dist/server/emit.d.ts +63 -0
- package/dist/server/emit.d.ts.map +1 -0
- package/dist/server/emit.js +101 -0
- package/dist/server/everyApp.d.ts +38 -12
- package/dist/server/everyApp.d.ts.map +1 -1
- package/dist/server/everyApp.js +92 -28
- package/dist/server/gatewayFetch.d.ts +24 -8
- package/dist/server/gatewayFetch.d.ts.map +1 -1
- package/dist/server/gatewayFetch.js +81 -36
- package/dist/server/index.d.ts +10 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +8 -1
- package/dist/server/integrationFetch.d.ts +131 -0
- package/dist/server/integrationFetch.d.ts.map +1 -0
- package/dist/server/integrationFetch.js +526 -0
- package/dist/server/listMembers.d.ts +32 -0
- package/dist/server/listMembers.d.ts.map +1 -0
- package/dist/server/listMembers.js +74 -0
- package/dist/server/nodeServer.d.ts +48 -0
- package/dist/server/nodeServer.d.ts.map +1 -0
- package/dist/server/nodeServer.js +45 -0
- package/dist/server/sendEmail.d.ts +39 -0
- package/dist/server/sendEmail.d.ts.map +1 -0
- package/dist/server/sendEmail.js +114 -0
- package/dist/server/tools.d.ts +184 -0
- package/dist/server/tools.d.ts.map +1 -0
- package/dist/server/tools.js +347 -0
- package/dist/server/toolsEndpoint.d.ts +32 -0
- package/dist/server/toolsEndpoint.d.ts.map +1 -0
- package/dist/server/toolsEndpoint.js +231 -0
- package/package.json +19 -6
- package/dist/server/mcp.d.ts +0 -66
- package/dist/server/mcp.d.ts.map +0 -1
- package/dist/server/mcp.js +0 -147
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sign-in providers a Gateway can offer beside passwords ("Continue
|
|
3
|
+
* with Google"), as data: what each one is called, the Worker secrets it
|
|
4
|
+
* reads, where the OAuth app is made, the scopes asked for and which claim
|
|
5
|
+
* becomes the email. One catalog serves the Gateway (which providers are
|
|
6
|
+
* on, the sign-in page's buttons, Manage workspace → Sign-in), the CLI
|
|
7
|
+
* (`everyapp signin add|list|show|remove`, `secret set`), the manifest
|
|
8
|
+
* validator (an app's own secret can never take one of these names) and
|
|
9
|
+
* the docs' drift tests.
|
|
10
|
+
*
|
|
11
|
+
* Naming rule, agreed with the provider registry's conventions
|
|
12
|
+
* (docs/design/2026-10-provider-registry.md, section 6): a sign-in
|
|
13
|
+
* provider's settings are `<ID>_SIGNIN_<FIELD>`, with FIELD one of
|
|
14
|
+
* CLIENT_ID, CLIENT_SECRET, ISSUER, LABEL or TENANT. The `_SIGNIN_` infix is
|
|
15
|
+
* what keeps the family apart from the API providers' `<ID>_API_KEY`,
|
|
16
|
+
* `<ID>_CLIENT_ID` (the OAuth clients the Gateway holds for apps) and the
|
|
17
|
+
* MCP apps' `<ID>_MCP_TOKEN`: `GOOGLE_SIGNIN_CLIENT_ID` is the sign-in
|
|
18
|
+
* provider's, `GOOGLE_WORKSPACE_CLIENT_ID` the OAuth client apps use, and
|
|
19
|
+
* a registered provider row or an app manifest can never claim a name in
|
|
20
|
+
* the family (isSignInSettingName).
|
|
21
|
+
*
|
|
22
|
+
* The settings are Worker secrets, set under the Cloudflare login (`everyapp
|
|
23
|
+
* signin add`, or `secret set`), never from the admin UI or an agent
|
|
24
|
+
* connection: a stolen session can't add a way in.
|
|
25
|
+
*/
|
|
26
|
+
export const SIGN_IN_PROVIDER_IDS = [
|
|
27
|
+
"google",
|
|
28
|
+
"microsoft",
|
|
29
|
+
"github",
|
|
30
|
+
"apple",
|
|
31
|
+
"cloudflare",
|
|
32
|
+
"oidc",
|
|
33
|
+
];
|
|
34
|
+
/** The path Better Auth serves the provider's callback on, under the Gateway's URL. */
|
|
35
|
+
export function signInCallbackPath(id) {
|
|
36
|
+
return `/api/auth/callback/${id}`;
|
|
37
|
+
}
|
|
38
|
+
/** The redirect URL to register with the provider, for a Gateway at `gatewayUrl`. */
|
|
39
|
+
export function signInCallbackUrl(gatewayUrl, id) {
|
|
40
|
+
return `${gatewayUrl.replace(/\/+$/, "")}${signInCallbackPath(id)}`;
|
|
41
|
+
}
|
|
42
|
+
function settingName(id, field) {
|
|
43
|
+
return `${id.toUpperCase()}_SIGNIN_${field}`;
|
|
44
|
+
}
|
|
45
|
+
function clientPair(id, what) {
|
|
46
|
+
return [
|
|
47
|
+
{
|
|
48
|
+
name: settingName(id, "CLIENT_ID"),
|
|
49
|
+
role: "clientId",
|
|
50
|
+
required: true,
|
|
51
|
+
plain: true,
|
|
52
|
+
what: what.clientId,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: settingName(id, "CLIENT_SECRET"),
|
|
56
|
+
role: "clientSecret",
|
|
57
|
+
required: true,
|
|
58
|
+
plain: false,
|
|
59
|
+
what: what.clientSecret,
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
const IDENTITY_SCOPES = ["openid", "email", "profile"];
|
|
64
|
+
export const SIGN_IN_PROVIDERS = {
|
|
65
|
+
google: {
|
|
66
|
+
id: "google",
|
|
67
|
+
label: "Google",
|
|
68
|
+
console: "https://console.cloud.google.com/apis/credentials",
|
|
69
|
+
appNoun: "OAuth client ID (Web application)",
|
|
70
|
+
settings: clientPair("google", {
|
|
71
|
+
clientId: "The OAuth client's Client ID (ends in .apps.googleusercontent.com)",
|
|
72
|
+
clientSecret: "The OAuth client's Client secret",
|
|
73
|
+
}),
|
|
74
|
+
scopes: IDENTITY_SCOPES,
|
|
75
|
+
emailClaim: "the ID token's `email`, counted as verified only when its `email_verified` is true (every Google account's own address is)",
|
|
76
|
+
setup: "setup/signin-google.md",
|
|
77
|
+
notes: [
|
|
78
|
+
"An OAuth consent screen still in Testing only signs in the test users listed on it; publish it for everyone else.",
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
microsoft: {
|
|
82
|
+
id: "microsoft",
|
|
83
|
+
label: "Microsoft",
|
|
84
|
+
console: "https://entra.microsoft.com/#view/Microsoft_AAD_RegisteredApps/ApplicationsListBlade",
|
|
85
|
+
appNoun: "App registration",
|
|
86
|
+
settings: [
|
|
87
|
+
...clientPair("microsoft", {
|
|
88
|
+
clientId: "The app registration's Application (client) ID",
|
|
89
|
+
clientSecret: "A client secret from Certificates & secrets (it expires; 24 months at most)",
|
|
90
|
+
}),
|
|
91
|
+
{
|
|
92
|
+
name: settingName("microsoft", "TENANT"),
|
|
93
|
+
role: "tenant",
|
|
94
|
+
required: false,
|
|
95
|
+
plain: true,
|
|
96
|
+
what: "The Directory (tenant) ID to pin sign-in to your own directory; unset means any Microsoft account (common), which then needs the xms_edov claim",
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
scopes: IDENTITY_SCOPES,
|
|
100
|
+
emailClaim: "the ID token's `email`, counted as verified when MICROSOFT_SIGNIN_TENANT pins your own directory, or the token carries `xms_edov: true` (the optional claim that says the domain's owner verified it), `email_verified: true`, a matching `verified_primary_email`, or comes from a personal Microsoft account",
|
|
101
|
+
setup: "setup/signin-microsoft.md",
|
|
102
|
+
notes: [
|
|
103
|
+
"Without MICROSOFT_SIGNIN_TENANT, add the optional claims `email` and `xms_edov` under Token configuration, or every sign-in is refused as unverified: any tenant's admin can put any address on their users.",
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
github: {
|
|
107
|
+
id: "github",
|
|
108
|
+
label: "GitHub",
|
|
109
|
+
console: "https://github.com/settings/developers",
|
|
110
|
+
appNoun: "OAuth App",
|
|
111
|
+
settings: clientPair("github", {
|
|
112
|
+
clientId: "The OAuth App's Client ID",
|
|
113
|
+
clientSecret: "A client secret generated on the OAuth App",
|
|
114
|
+
}),
|
|
115
|
+
scopes: ["read:user", "user:email"],
|
|
116
|
+
emailClaim: "the primary address from GitHub's emails API, counted as verified only when GitHub marks it verified",
|
|
117
|
+
setup: "setup/signin-github.md",
|
|
118
|
+
notes: [
|
|
119
|
+
"GitHub Apps are something else: make an OAuth App (Settings → Developer settings → OAuth Apps).",
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
apple: {
|
|
123
|
+
id: "apple",
|
|
124
|
+
label: "Apple",
|
|
125
|
+
console: "https://developer.apple.com/account/resources/identifiers/list/serviceId",
|
|
126
|
+
appNoun: "Services ID",
|
|
127
|
+
settings: clientPair("apple", {
|
|
128
|
+
clientId: "The Services ID's identifier (com.example.gateway)",
|
|
129
|
+
clientSecret: "The client secret JWT you generate from your Sign in with Apple key (ES256; valid six months at most, then generate it again)",
|
|
130
|
+
}),
|
|
131
|
+
scopes: ["email", "name"],
|
|
132
|
+
emailClaim: "the ID token's `email`, counted as verified only when its `email_verified` is true; someone who chose Hide My Email arrives as a @privaterelay.appleid.com address, which no invitation names",
|
|
133
|
+
setup: "setup/signin-apple.md",
|
|
134
|
+
notes: [
|
|
135
|
+
"Apple returns to the Gateway with a POST; behind the outer ring, `everyapp access on` leaves /api/auth/callback/apple public (it only redirects to the same URL as a GET, which Access then checks).",
|
|
136
|
+
"The client secret is a JWT that expires within six months: generate a new one before then or sign-in stops.",
|
|
137
|
+
],
|
|
138
|
+
},
|
|
139
|
+
cloudflare: {
|
|
140
|
+
id: "cloudflare",
|
|
141
|
+
label: "Cloudflare",
|
|
142
|
+
console: "https://dash.cloudflare.com/?to=/:account/oauth-clients",
|
|
143
|
+
appNoun: "OAuth client (Manage Account → OAuth clients)",
|
|
144
|
+
settings: clientPair("cloudflare", {
|
|
145
|
+
clientId: "The OAuth client's Client ID",
|
|
146
|
+
clientSecret: "The OAuth client's Client secret",
|
|
147
|
+
}),
|
|
148
|
+
scopes: ["user-details.read"],
|
|
149
|
+
emailClaim: "the Cloudflare API's /user `email` (the account's sign-in address). Cloudflare reports no verified flag, so the email counts as verified on the strength of the client being private: only members of your Cloudflare account can authorize it, and they got in by accepting an invitation sent to that address",
|
|
150
|
+
setup: "setup/signin-cloudflare.md",
|
|
151
|
+
notes: [
|
|
152
|
+
"Keep the client private (the default): a public client lets any Cloudflare user sign in, and Cloudflare says nothing about whether their address is verified. Making a client public is permanent.",
|
|
153
|
+
"The Cloudflare account's members sign in; for everyone else use Cloudflare Access's one-time PIN (`everyapp access on`) or another provider.",
|
|
154
|
+
],
|
|
155
|
+
},
|
|
156
|
+
oidc: {
|
|
157
|
+
id: "oidc",
|
|
158
|
+
label: "SSO",
|
|
159
|
+
console: "your identity provider's admin console",
|
|
160
|
+
appNoun: "OpenID Connect client (web application, authorization code)",
|
|
161
|
+
settings: [
|
|
162
|
+
{
|
|
163
|
+
name: settingName("oidc", "ISSUER"),
|
|
164
|
+
role: "issuer",
|
|
165
|
+
required: true,
|
|
166
|
+
plain: true,
|
|
167
|
+
what: "The issuer URL (https://…), which serves /.well-known/openid-configuration",
|
|
168
|
+
},
|
|
169
|
+
...clientPair("oidc", {
|
|
170
|
+
clientId: "The client ID",
|
|
171
|
+
clientSecret: "The client secret",
|
|
172
|
+
}),
|
|
173
|
+
{
|
|
174
|
+
name: settingName("oidc", "LABEL"),
|
|
175
|
+
role: "label",
|
|
176
|
+
required: false,
|
|
177
|
+
plain: true,
|
|
178
|
+
what: 'The button text after "Continue with" (default "SSO")',
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
scopes: IDENTITY_SCOPES,
|
|
182
|
+
emailClaim: "the ID token's `email` (else the userinfo endpoint's), counted as verified only when `email_verified` is true",
|
|
183
|
+
setup: "setup/signin-oidc.md",
|
|
184
|
+
notes: [
|
|
185
|
+
"Cloudflare Access has its own settings (CLOUDFLARE_ACCESS_OIDC_*, `everyapp access`): use those for Access, this for Okta, Entra ID, Google Workspace as an OIDC issuer, Keycloak, Auth0 and the like.",
|
|
186
|
+
"ID tokens must be signed with RS256 (every major provider's default).",
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
/** The catalog in listing order. */
|
|
191
|
+
export const SIGN_IN_PROVIDER_LIST = SIGN_IN_PROVIDER_IDS.map((id) => SIGN_IN_PROVIDERS[id]);
|
|
192
|
+
export function isSignInProviderId(value) {
|
|
193
|
+
return SIGN_IN_PROVIDER_IDS.includes(value);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Every setting a sign-in provider reads, in catalog order: what `everyapp
|
|
197
|
+
* secret set` accepts for the Gateway, and what the Gateway's dev config
|
|
198
|
+
* declares.
|
|
199
|
+
*/
|
|
200
|
+
export const SIGN_IN_GATEWAY_SETTINGS = SIGN_IN_PROVIDER_LIST.flatMap((provider) => provider.settings.map((setting) => setting.name));
|
|
201
|
+
/** The plain (non-credential) ones among them: ids, the issuer, the label, the tenant. */
|
|
202
|
+
export const PLAIN_SIGN_IN_SETTINGS = SIGN_IN_PROVIDER_LIST.flatMap((provider) => provider.settings
|
|
203
|
+
.filter((setting) => setting.plain)
|
|
204
|
+
.map((setting) => setting.name));
|
|
205
|
+
/**
|
|
206
|
+
* The family: any name of the shape `<ID>_SIGNIN_<FIELD>`. Reserved for the
|
|
207
|
+
* catalog above, whether or not an entry reads the name today, so no
|
|
208
|
+
* registered provider row and no app manifest can ever take one.
|
|
209
|
+
*/
|
|
210
|
+
export const SIGN_IN_SETTING_PATTERN = /^[A-Z][A-Z0-9_]*_SIGNIN_(CLIENT_ID|CLIENT_SECRET|ISSUER|LABEL|TENANT)$/;
|
|
211
|
+
export function isSignInSettingName(name) {
|
|
212
|
+
return SIGN_IN_SETTING_PATTERN.test(name);
|
|
213
|
+
}
|
|
214
|
+
/** The provider a setting belongs to, if one reads it. */
|
|
215
|
+
export function signInProviderOfSetting(name) {
|
|
216
|
+
for (const provider of SIGN_IN_PROVIDER_LIST) {
|
|
217
|
+
if (provider.settings.some((setting) => setting.name === name)) {
|
|
218
|
+
return provider;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* A provider's state from which of its settings exist: on when every
|
|
225
|
+
* required one is set, off when none at all is, incomplete in between (the
|
|
226
|
+
* Gateway refuses to half-configure a sign-in: the provider is off and the
|
|
227
|
+
* problem is reported).
|
|
228
|
+
*/
|
|
229
|
+
export function signInProviderState(provider, isSet) {
|
|
230
|
+
const set = provider.settings
|
|
231
|
+
.filter((setting) => isSet(setting.name))
|
|
232
|
+
.map((setting) => setting.name);
|
|
233
|
+
const missing = provider.settings
|
|
234
|
+
.filter((setting) => setting.required && !isSet(setting.name))
|
|
235
|
+
.map((setting) => setting.name);
|
|
236
|
+
const status = missing.length === 0 ? "on" : set.length === 0 ? "off" : "incomplete";
|
|
237
|
+
return { id: provider.id, status, missing, set };
|
|
238
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The internal tools contract (v1) between the gateway's ToolBus and an app's
|
|
3
|
+
* `everyApp()` wrapper. Two reserved endpoints, reached only over the
|
|
4
|
+
* gateway's service binding and never public:
|
|
5
|
+
*
|
|
6
|
+
* POST /__everyapp/tools/list x-everyapp-system JWT (purpose "tools.list")
|
|
7
|
+
* POST /__everyapp/tools/call x-everyapp-identity JWT, chan "mcp" + tc claim
|
|
8
|
+
*
|
|
9
|
+
* MCP itself (protocol eras, headers, envelopes) stays in the gateway; apps
|
|
10
|
+
* only ever speak this small versioned JSON shape. Both sides import the
|
|
11
|
+
* shapes, paths and the catalog hash from here so they cannot drift.
|
|
12
|
+
*/
|
|
13
|
+
export declare const TOOLS_PROTOCOL_VERSION = 1;
|
|
14
|
+
export declare const TOOLS_PATH_PREFIX = "/__everyapp/tools";
|
|
15
|
+
export declare const TOOLS_LIST_PATH = "/__everyapp/tools/list";
|
|
16
|
+
export declare const TOOLS_CALL_PATH = "/__everyapp/tools/call";
|
|
17
|
+
export declare const TOOL_RISKS: readonly ["read", "write", "destructive"];
|
|
18
|
+
export type ToolRisk = (typeof TOOL_RISKS)[number];
|
|
19
|
+
/** Scopes the gateway mints on `chan: "mcp"` identities, one per risk. */
|
|
20
|
+
export declare const MCP_READ_SCOPE = "mcp:read";
|
|
21
|
+
export declare const MCP_WRITE_SCOPE = "mcp:write";
|
|
22
|
+
/** Never minted in v1: destructive tools are hidden until approvals exist. */
|
|
23
|
+
export declare const MCP_DESTRUCTIVE_SCOPE = "mcp:destructive";
|
|
24
|
+
/** Local tool names: lowercase snake_case, at most 48 characters. */
|
|
25
|
+
export declare const TOOL_NAME_PATTERN: RegExp;
|
|
26
|
+
export declare function isToolRisk(value: unknown): value is ToolRisk;
|
|
27
|
+
/** The exact scope a `chan: "mcp"` identity must carry to call a tool. */
|
|
28
|
+
export declare function requiredScopeForRisk(risk: ToolRisk): string;
|
|
29
|
+
export interface ToolAnnotationsV1 {
|
|
30
|
+
idempotentHint?: boolean;
|
|
31
|
+
openWorldHint?: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** One tool as an app describes it to the gateway. */
|
|
34
|
+
export interface ToolDescriptorV1 {
|
|
35
|
+
name: string;
|
|
36
|
+
title?: string;
|
|
37
|
+
description: string;
|
|
38
|
+
risk: ToolRisk;
|
|
39
|
+
/** JSON Schema (draft 2020-12) of the arguments; always `type: "object"`. */
|
|
40
|
+
inputSchema: Record<string, unknown>;
|
|
41
|
+
outputSchema?: Record<string, unknown>;
|
|
42
|
+
annotations?: ToolAnnotationsV1;
|
|
43
|
+
}
|
|
44
|
+
export interface ToolsListRequestV1 {
|
|
45
|
+
v: 1;
|
|
46
|
+
}
|
|
47
|
+
export interface ToolsListResponseV1 {
|
|
48
|
+
v: 1;
|
|
49
|
+
/** The SDK version that produced the catalog. */
|
|
50
|
+
sdk: string;
|
|
51
|
+
/** {@link catalogHash} of `instructions` + `tools`. */
|
|
52
|
+
hash: string;
|
|
53
|
+
instructions?: string;
|
|
54
|
+
tools: ToolDescriptorV1[];
|
|
55
|
+
/** Tools the SDK left out because it can't describe them, and why. */
|
|
56
|
+
warnings?: string[];
|
|
57
|
+
}
|
|
58
|
+
/** The `tc` identity claim: binds a `chan: "mcp"` token to one call. */
|
|
59
|
+
export interface ToolCallClaim {
|
|
60
|
+
id: string;
|
|
61
|
+
name: string;
|
|
62
|
+
}
|
|
63
|
+
export interface ToolsCallRequestV1 {
|
|
64
|
+
v: 1;
|
|
65
|
+
name: string;
|
|
66
|
+
arguments: Record<string, unknown>;
|
|
67
|
+
/** Must equal the identity token's `tc.id`. */
|
|
68
|
+
callId: string;
|
|
69
|
+
}
|
|
70
|
+
export interface ToolContentBlockV1 {
|
|
71
|
+
type: string;
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
74
|
+
export interface ToolCallResultV1 {
|
|
75
|
+
content: ToolContentBlockV1[];
|
|
76
|
+
structuredContent?: Record<string, unknown>;
|
|
77
|
+
isError?: boolean;
|
|
78
|
+
}
|
|
79
|
+
export type ToolsErrorCodeV1 = "unknown_tool" | "invalid_arguments" | "invalid_request" | "forbidden" | "unauthenticated" | "method_not_allowed" | "misconfigured";
|
|
80
|
+
export interface ToolIssueV1 {
|
|
81
|
+
message: string;
|
|
82
|
+
path?: (string | number)[];
|
|
83
|
+
}
|
|
84
|
+
export interface ToolsErrorV1 {
|
|
85
|
+
code: ToolsErrorCodeV1;
|
|
86
|
+
message: string;
|
|
87
|
+
issues?: ToolIssueV1[];
|
|
88
|
+
}
|
|
89
|
+
export interface ToolsCallSuccessV1 {
|
|
90
|
+
v: 1;
|
|
91
|
+
/** The app's current catalog hash; the gateway re-syncs on a mismatch. */
|
|
92
|
+
catalogHash: string;
|
|
93
|
+
result: ToolCallResultV1;
|
|
94
|
+
}
|
|
95
|
+
export interface ToolsErrorResponseV1 {
|
|
96
|
+
v: 1;
|
|
97
|
+
error: ToolsErrorV1;
|
|
98
|
+
}
|
|
99
|
+
export type ToolsCallResponseV1 = ToolsCallSuccessV1 | ToolsErrorResponseV1;
|
|
100
|
+
/**
|
|
101
|
+
* JSON with object keys sorted at every depth, so equal values always
|
|
102
|
+
* serialize identically. `undefined` members are dropped, as JSON.stringify
|
|
103
|
+
* does.
|
|
104
|
+
*/
|
|
105
|
+
export declare function canonicalJson(value: unknown): string;
|
|
106
|
+
/**
|
|
107
|
+
* `sha256:<hex>` over the canonical JSON of a catalog. The SDK reports it with
|
|
108
|
+
* every list and call; the gateway stores it at sync and re-syncs when a call
|
|
109
|
+
* reports a different one.
|
|
110
|
+
*/
|
|
111
|
+
export declare function catalogHash(catalog: {
|
|
112
|
+
instructions?: string;
|
|
113
|
+
tools: readonly ToolDescriptorV1[];
|
|
114
|
+
}): Promise<string>;
|
|
115
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/internal/tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The internal tools contract (v1) between the gateway's ToolBus and an app's
|
|
3
|
+
* `everyApp()` wrapper. Two reserved endpoints, reached only over the
|
|
4
|
+
* gateway's service binding and never public:
|
|
5
|
+
*
|
|
6
|
+
* POST /__everyapp/tools/list x-everyapp-system JWT (purpose "tools.list")
|
|
7
|
+
* POST /__everyapp/tools/call x-everyapp-identity JWT, chan "mcp" + tc claim
|
|
8
|
+
*
|
|
9
|
+
* MCP itself (protocol eras, headers, envelopes) stays in the gateway; apps
|
|
10
|
+
* only ever speak this small versioned JSON shape. Both sides import the
|
|
11
|
+
* shapes, paths and the catalog hash from here so they cannot drift.
|
|
12
|
+
*/
|
|
13
|
+
export const TOOLS_PROTOCOL_VERSION = 1;
|
|
14
|
+
export const TOOLS_PATH_PREFIX = "/__everyapp/tools";
|
|
15
|
+
export const TOOLS_LIST_PATH = `${TOOLS_PATH_PREFIX}/list`;
|
|
16
|
+
export const TOOLS_CALL_PATH = `${TOOLS_PATH_PREFIX}/call`;
|
|
17
|
+
export const TOOL_RISKS = ["read", "write", "destructive"];
|
|
18
|
+
/** Scopes the gateway mints on `chan: "mcp"` identities, one per risk. */
|
|
19
|
+
export const MCP_READ_SCOPE = "mcp:read";
|
|
20
|
+
export const MCP_WRITE_SCOPE = "mcp:write";
|
|
21
|
+
/** Never minted in v1: destructive tools are hidden until approvals exist. */
|
|
22
|
+
export const MCP_DESTRUCTIVE_SCOPE = "mcp:destructive";
|
|
23
|
+
/** Local tool names: lowercase snake_case, at most 48 characters. */
|
|
24
|
+
export const TOOL_NAME_PATTERN = /^[a-z][a-z0-9_]{0,47}$/;
|
|
25
|
+
export function isToolRisk(value) {
|
|
26
|
+
return (typeof value === "string" &&
|
|
27
|
+
TOOL_RISKS.includes(value));
|
|
28
|
+
}
|
|
29
|
+
/** The exact scope a `chan: "mcp"` identity must carry to call a tool. */
|
|
30
|
+
export function requiredScopeForRisk(risk) {
|
|
31
|
+
switch (risk) {
|
|
32
|
+
case "read":
|
|
33
|
+
return MCP_READ_SCOPE;
|
|
34
|
+
case "write":
|
|
35
|
+
return MCP_WRITE_SCOPE;
|
|
36
|
+
case "destructive":
|
|
37
|
+
return MCP_DESTRUCTIVE_SCOPE;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* JSON with object keys sorted at every depth, so equal values always
|
|
42
|
+
* serialize identically. `undefined` members are dropped, as JSON.stringify
|
|
43
|
+
* does.
|
|
44
|
+
*/
|
|
45
|
+
export function canonicalJson(value) {
|
|
46
|
+
return JSON.stringify(sortKeys(value));
|
|
47
|
+
}
|
|
48
|
+
function sortKeys(value) {
|
|
49
|
+
if (Array.isArray(value))
|
|
50
|
+
return value.map(sortKeys);
|
|
51
|
+
if (!value || typeof value !== "object")
|
|
52
|
+
return value;
|
|
53
|
+
const sorted = {};
|
|
54
|
+
for (const key of Object.keys(value).sort()) {
|
|
55
|
+
const member = value[key];
|
|
56
|
+
if (member !== undefined)
|
|
57
|
+
sorted[key] = sortKeys(member);
|
|
58
|
+
}
|
|
59
|
+
return sorted;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* `sha256:<hex>` over the canonical JSON of a catalog. The SDK reports it with
|
|
63
|
+
* every list and call; the gateway stores it at sync and re-syncs when a call
|
|
64
|
+
* reports a different one.
|
|
65
|
+
*/
|
|
66
|
+
export async function catalogHash(catalog) {
|
|
67
|
+
const canonical = canonicalJson({
|
|
68
|
+
instructions: catalog.instructions ?? null,
|
|
69
|
+
tools: catalog.tools,
|
|
70
|
+
});
|
|
71
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(canonical));
|
|
72
|
+
return `sha256:${toHex(digest)}`;
|
|
73
|
+
}
|
|
74
|
+
function toHex(buffer) {
|
|
75
|
+
let out = "";
|
|
76
|
+
for (const byte of new Uint8Array(buffer)) {
|
|
77
|
+
out += byte.toString(16).padStart(2, "0");
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whom a call through the gateway is for, which decides the organization
|
|
3
|
+
* it counts against:
|
|
4
|
+
*
|
|
5
|
+
* - the inbound `Request`: its person, by the identity the gateway put on
|
|
6
|
+
* it (forwarded as is);
|
|
7
|
+
* - `{ background: true, organizationId }`: work no request started (a
|
|
8
|
+
* cron, a Workflow step, a queue). A shared app's Worker serves every
|
|
9
|
+
* organization, so it names the one the work is for, which the gateway
|
|
10
|
+
* checks is active and has a live member. An organization's own app may
|
|
11
|
+
* leave it out (the gateway knows its organization) and may only name
|
|
12
|
+
* its own.
|
|
13
|
+
*/
|
|
14
|
+
export type GatewayCallFor = Request | {
|
|
15
|
+
background: true;
|
|
16
|
+
organizationId?: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* A gateway call that doesn't say whom it is for where it must: from a
|
|
20
|
+
* shared app's Worker, with neither the request nor an organization.
|
|
21
|
+
*/
|
|
22
|
+
export declare class AttributionError extends Error {
|
|
23
|
+
readonly code = "attribution_required";
|
|
24
|
+
constructor(message: string);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Check `target` before any call is made: a shared app's Worker must pass
|
|
28
|
+
* the request or name the organization, and anything else passed must be
|
|
29
|
+
* one of the two shapes.
|
|
30
|
+
*/
|
|
31
|
+
export declare function checkAttribution(env: object, target: GatewayCallFor | undefined, what: string): void;
|
|
32
|
+
/**
|
|
33
|
+
* Put the call's attribution on its headers: the request's identity, or the
|
|
34
|
+
* background work's organization, and never one the app set itself.
|
|
35
|
+
*/
|
|
36
|
+
export declare function applyAttribution(headers: Headers, target: GatewayCallFor | undefined): void;
|
|
37
|
+
/** Whether `for` is the inbound request (rather than background work). */
|
|
38
|
+
export declare function isRequest(value: unknown): value is Request;
|
|
39
|
+
//# sourceMappingURL=attribution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../../src/server/attribution.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,cAAc,GACtB,OAAO,GACP;IAAE,UAAU,EAAE,IAAI,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAElD;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,IAAI,0BAA0B;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,cAAc,GAAG,SAAS,EAClC,IAAI,EAAE,MAAM,GACX,IAAI,CAsBN;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,cAAc,GAAG,SAAS,GACjC,IAAI,CAUN;AASD,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAO1D"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { IDENTITY_HEADER, ORG_HEADER, SHARED_ENV } from "../internal/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* A gateway call that doesn't say whom it is for where it must: from a
|
|
4
|
+
* shared app's Worker, with neither the request nor an organization.
|
|
5
|
+
*/
|
|
6
|
+
export class AttributionError extends Error {
|
|
7
|
+
code = "attribution_required";
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "AttributionError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Check `target` before any call is made: a shared app's Worker must pass
|
|
15
|
+
* the request or name the organization, and anything else passed must be
|
|
16
|
+
* one of the two shapes.
|
|
17
|
+
*/
|
|
18
|
+
export function checkAttribution(env, target, what) {
|
|
19
|
+
if (target !== undefined && !isRequest(target)) {
|
|
20
|
+
if (target === null || typeof target !== "object" || !target.background) {
|
|
21
|
+
throw new AttributionError(`${what}: \`for\` is the inbound Request or { background: true, organizationId }.`);
|
|
22
|
+
}
|
|
23
|
+
if (target.organizationId !== undefined &&
|
|
24
|
+
!nonEmpty(target.organizationId)) {
|
|
25
|
+
throw new AttributionError(`${what}: { background: true, organizationId } needs a non-empty organization id.`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (!isSharedWorker(env))
|
|
29
|
+
return;
|
|
30
|
+
if (target === undefined || (!isRequest(target) && !target.organizationId)) {
|
|
31
|
+
throw new AttributionError(`${what} in a shared app's Worker serves every organization, so it must say whom the call is for: for: request, or for: { background: true, organizationId } for work no request started.`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Put the call's attribution on its headers: the request's identity, or the
|
|
36
|
+
* background work's organization, and never one the app set itself.
|
|
37
|
+
*/
|
|
38
|
+
export function applyAttribution(headers, target) {
|
|
39
|
+
headers.delete(IDENTITY_HEADER);
|
|
40
|
+
headers.delete(ORG_HEADER);
|
|
41
|
+
if (target === undefined)
|
|
42
|
+
return;
|
|
43
|
+
if (isRequest(target)) {
|
|
44
|
+
const identity = target.headers.get(IDENTITY_HEADER);
|
|
45
|
+
if (identity)
|
|
46
|
+
headers.set(IDENTITY_HEADER, identity);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (target.organizationId)
|
|
50
|
+
headers.set(ORG_HEADER, target.organizationId);
|
|
51
|
+
}
|
|
52
|
+
function isSharedWorker(env) {
|
|
53
|
+
const value = env[SHARED_ENV];
|
|
54
|
+
return value === "1" || value === "true" || value === true;
|
|
55
|
+
}
|
|
56
|
+
// Duck-typed: a Request from another realm (a test runner, a polyfill) is
|
|
57
|
+
// still the request.
|
|
58
|
+
/** Whether `for` is the inbound request (rather than background work). */
|
|
59
|
+
export function isRequest(value) {
|
|
60
|
+
return (typeof value === "object" &&
|
|
61
|
+
value !== null &&
|
|
62
|
+
"headers" in value &&
|
|
63
|
+
typeof value.headers?.get === "function");
|
|
64
|
+
}
|
|
65
|
+
function nonEmpty(value) {
|
|
66
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
67
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type OAuthClientCapabilityId } from "../internal/index.js";
|
|
2
|
+
import type { OAuthClientIntegrationStatus } from "./integrationFetch.js";
|
|
3
|
+
export interface AuthorizeUrlOptions {
|
|
4
|
+
/** The OAuth client, as listed in the manifest's `capabilities`. */
|
|
5
|
+
provider: OAuthClientCapabilityId;
|
|
6
|
+
/** What `integrationStatus()` answered for it, ready and with its `clientId`. */
|
|
7
|
+
status: OAuthClientIntegrationStatus;
|
|
8
|
+
/** One of the redirect URIs the operator registered on the client. */
|
|
9
|
+
redirectUri: string;
|
|
10
|
+
/** The app's own anti-forgery value, checked when the person comes back. */
|
|
11
|
+
state: string;
|
|
12
|
+
/** PKCE: the S256 challenge of the verifier the app keeps for the exchange. */
|
|
13
|
+
codeChallenge?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The provider's extras (`prompt`, `login_hint`, Google's `hd`). Never
|
|
16
|
+
* one of the parameters this helper sets (PKCE's and the provider's own
|
|
17
|
+
* included), nor Google's `include_granted_scopes`.
|
|
18
|
+
*/
|
|
19
|
+
params?: Record<string, string>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The provider's authorize URL to send a person to, for a client with
|
|
23
|
+
* scope sets (`google-workspace`, `microsoft-365`, `slack`), built from the
|
|
24
|
+
* capability's status: the registry's authorize endpoint (or the instance
|
|
25
|
+
* the status names), `client_id`, `response_type=code`, `redirect_uri`,
|
|
26
|
+
* `state`, and the scopes the status allows this app, which are exactly
|
|
27
|
+
* what the gateway will let the token carry. Each provider's own needs
|
|
28
|
+
* are added: Google's `access_type=offline` (a refresh token), Microsoft's
|
|
29
|
+
* `response_mode=query`, Slack's comma-joined `scope` and its `user_scope`
|
|
30
|
+
* (the status's `userScopes`, when the app's sets include a user set). With
|
|
31
|
+
* `codeChallenge`, PKCE as S256. The app keeps its `state` and verifier
|
|
32
|
+
* for the callback.
|
|
33
|
+
*
|
|
34
|
+
* A client without scope sets (LinkedIn, Mastodon, ...) is refused: the
|
|
35
|
+
* registry's checklist for one isn't what the gateway holds a token to,
|
|
36
|
+
* so an app builds that URL itself from the status's `clientId`.
|
|
37
|
+
*
|
|
38
|
+
* Throws a `TypeError` naming the fix when the client has no scope sets,
|
|
39
|
+
* when the status isn't ready or has no `clientId`, when the status lacks
|
|
40
|
+
* `scopes`, or when `params` names a parameter the helper sets.
|
|
41
|
+
*/
|
|
42
|
+
export declare function authorizeUrl({ provider, status, redirectUri, state, codeChallenge, params, }: AuthorizeUrlOptions): URL;
|
|
43
|
+
//# sourceMappingURL=authorizeUrl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authorizeUrl.d.ts","sourceRoot":"","sources":["../../src/server/authorizeUrl.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AAE1E,MAAM,WAAW,mBAAmB;IAClC,oEAAoE;IACpE,QAAQ,EAAE,uBAAuB,CAAC;IAClC,iFAAiF;IACjF,MAAM,EAAE,4BAA4B,CAAC;IACrC,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAgCD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,MAAM,EACN,WAAW,EACX,KAAK,EACL,aAAa,EACb,MAAM,GACP,EAAE,mBAAmB,GAAG,GAAG,CA2D3B"}
|