@segmentstream-cloud/ontology 0.9.0 → 0.11.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 (90) hide show
  1. package/dist/action.d.ts +19 -0
  2. package/dist/action.js +1 -0
  3. package/dist/analyze.d.ts +3 -1
  4. package/dist/analyze.js +97 -2
  5. package/dist/bin/migrate.js +3 -2
  6. package/dist/blob.d.ts +39 -0
  7. package/dist/blob.js +49 -0
  8. package/dist/diagnostics.d.ts +3 -3
  9. package/dist/diagnostics.js +1 -0
  10. package/dist/entity.d.ts +5 -3
  11. package/dist/index.d.ts +9 -4
  12. package/dist/index.js +6 -1
  13. package/dist/manifest.d.ts +71 -7
  14. package/dist/manifest.js +18 -1
  15. package/dist/migrate/apply.d.ts +7 -0
  16. package/dist/migrate/apply.js +18 -1
  17. package/dist/migrate/index.d.ts +1 -1
  18. package/dist/migrate/index.js +1 -1
  19. package/dist/migrate/model.d.ts +15 -2
  20. package/dist/migrate/plan.d.ts +6 -0
  21. package/dist/migrate/plan.js +35 -3
  22. package/dist/migrate/storage.d.ts +14 -0
  23. package/dist/migrate/storage.js +95 -0
  24. package/dist/names.d.ts +9 -1
  25. package/dist/names.js +11 -0
  26. package/dist/nitro.d.ts +13 -0
  27. package/dist/nitro.js +18 -0
  28. package/dist/property.d.ts +10 -1
  29. package/dist/property.js +12 -1
  30. package/dist/query.js +6 -1
  31. package/dist/runtime/generated-actions.d.ts +3 -0
  32. package/dist/runtime/generated-actions.js +10 -5
  33. package/dist/runtime/history.d.ts +3 -0
  34. package/dist/runtime/oauth.d.ts +9 -0
  35. package/dist/runtime/oauth.js +165 -0
  36. package/dist/runtime/pipeline.d.ts +5 -1
  37. package/dist/runtime/pipeline.js +87 -3
  38. package/dist/runtime/principals.d.ts +5 -0
  39. package/dist/runtime/principals.js +15 -0
  40. package/dist/runtime/records.js +9 -0
  41. package/dist/runtime/service-binding.d.ts +2 -1
  42. package/dist/runtime/service-binding.js +15 -2
  43. package/dist/runtime/writes.js +17 -3
  44. package/dist/service/api.js +19 -10
  45. package/dist/service/authentication.d.ts +27 -1
  46. package/dist/service/authentication.js +48 -26
  47. package/dist/service/config.d.ts +8 -0
  48. package/dist/service/config.js +18 -0
  49. package/dist/service/descriptions.d.ts +8 -0
  50. package/dist/service/descriptions.js +15 -0
  51. package/dist/service/errors.d.ts +5 -1
  52. package/dist/service/errors.js +6 -2
  53. package/dist/service/handlers.d.ts +1 -4
  54. package/dist/service/handlers.js +59 -203
  55. package/dist/service/mcp/server.d.ts +24 -0
  56. package/dist/service/mcp/server.js +370 -0
  57. package/dist/service/mcp/tools.d.ts +47 -0
  58. package/dist/service/mcp/tools.js +218 -0
  59. package/dist/service/oauth/protocol.d.ts +52 -0
  60. package/dist/service/oauth/protocol.js +110 -0
  61. package/dist/service/oauth/server.d.ts +27 -0
  62. package/dist/service/oauth/server.js +576 -0
  63. package/dist/service/operations.d.ts +114 -0
  64. package/dist/service/operations.js +218 -0
  65. package/dist/service/runtime-contract.d.ts +98 -0
  66. package/dist/service/schemas.d.ts +18 -2
  67. package/dist/service/schemas.js +18 -2
  68. package/dist/service/service.d.ts +3 -3
  69. package/dist/service/service.js +27 -7
  70. package/dist/storage/api.d.ts +4 -0
  71. package/dist/storage/api.js +39 -0
  72. package/dist/storage/authorization.d.ts +8 -0
  73. package/dist/storage/authorization.js +30 -0
  74. package/dist/storage/maintenance-auth.d.ts +20 -0
  75. package/dist/storage/maintenance-auth.js +24 -0
  76. package/dist/storage/maintenance.d.ts +7 -0
  77. package/dist/storage/maintenance.js +50 -0
  78. package/dist/storage/neon.d.ts +3 -0
  79. package/dist/storage/neon.js +212 -0
  80. package/dist/storage/operator.d.ts +10 -0
  81. package/dist/storage/operator.js +30 -0
  82. package/dist/storage/provider.d.ts +80 -0
  83. package/dist/storage/provider.js +20 -0
  84. package/dist/storage/sessions.d.ts +87 -0
  85. package/dist/storage/sessions.js +341 -0
  86. package/dist/storage/tables.d.ts +2 -0
  87. package/dist/storage/tables.js +55 -0
  88. package/dist/storage/writes.d.ts +7 -0
  89. package/dist/storage/writes.js +88 -0
  90. package/package.json +8 -1
@@ -50,11 +50,15 @@ const REQUEST_PARTS = {
50
50
  function isJsonParseFailure(issue) {
51
51
  return (issue._tag === "InvalidValue" && issue.annotations?.message === "Expected a valid JSON body");
52
52
  }
53
- export function unauthorized(requestId, detail) {
53
+ /**
54
+ * `challenge` is the `WWW-Authenticate` value; the MCP server's names its OAuth metadata, which is
55
+ * how a client learns where members authorize it.
56
+ */
57
+ export function unauthorized(requestId, detail, challenge = 'Bearer realm="ontology"') {
54
58
  return {
55
59
  status: 401,
56
60
  body: { error: "unauthorized", detail, requestId },
57
- headers: { "www-authenticate": 'Bearer realm="ontology"' },
61
+ headers: { "www-authenticate": challenge },
58
62
  };
59
63
  }
60
64
  /**
@@ -1,13 +1,10 @@
1
1
  import { Layer } from "effect";
2
2
  import { type HttpApiGroup } from "effect/unstable/httpapi";
3
- import type { Query } from "../query.js";
4
3
  import { type BuiltApi } from "./api.js";
5
4
  import type { AfterCommitHook } from "./config.js";
6
- import { type ConnectionTransportFactory } from "./connections.js";
5
+ import type { ConnectionTransportFactory } from "./connections.js";
7
6
  import { type RateLimit } from "./rate-limit.js";
8
7
  import { OntologyBackend } from "./runtime-contract.js";
9
- /** The query the runtime receives: every key present only when set, and no empty `include`. */
10
- export declare function toQuery(decoded: Query): Query;
11
8
  /**
12
9
  * Sign-in checks one client may make per minute. A person checks once per sign-in, a few times at
13
10
  * most; the limit slows down anyone testing addresses to learn who the app's members are.
@@ -1,19 +1,15 @@
1
1
  import { HttpServerResponse } from "effect/unstable/http";
2
2
  import { Effect, Layer, Schema } from "effect";
3
3
  import { HttpApiBuilder, OpenApi } from "effect/unstable/httpapi";
4
- import { editsFromWire, generatedResultFromWire, recordFromWire } from "../authoring-records.js";
5
- import { nameToWire } from "../authoring-names.js";
6
- import { principalFromWire } from "../authoring.js";
7
- import { toCamelCase } from "../names.js";
8
- import { PermissionDenied } from "../errors.js";
9
- import { missingPermission, permissionOf, READ_VERB } from "../permissions.js";
4
+ import { STORAGE_GROUP } from "../storage/api.js";
5
+ import { Conflict } from "../errors.js";
10
6
  import { EMAIL_PATTERN } from "../property.js";
11
7
  import { renderCatalog } from "../render.js";
12
8
  import { SERVICE_GROUP } from "./api.js";
13
9
  import { CurrentPrincipal } from "./authentication.js";
14
10
  import { catalogFor, graphOf } from "./catalog.js";
15
- import { OIDC_TOKEN_HEADER } from "./connections.js";
16
- import { InvalidActionInput, InvalidRequestBody, RequestId, SignInCheckLimited, schemaIssues, } from "./errors.js";
11
+ import { InvalidRequestBody, SignInCheckLimited, schemaIssues } from "./errors.js";
12
+ import { identityToken, makeOperations, requireRead, toQuery, } from "./operations.js";
17
13
  import { clientAddress, fixedWindowLimiter, } from "./rate-limit.js";
18
14
  import { OntologyBackend } from "./runtime-contract.js";
19
15
  import { ActionEnvelope } from "./schemas.js";
@@ -21,102 +17,6 @@ function dynamicGroup(api, identifier, build) {
21
17
  const group = HttpApiBuilder.group;
22
18
  return group(api, identifier, build);
23
19
  }
24
- /** The query the runtime receives: every key present only when set, and no empty `include`. */
25
- export function toQuery(decoded) {
26
- const { include, ...rest } = decoded;
27
- return include !== undefined && Object.keys(include).length > 0 ? { ...rest, include } : rest;
28
- }
29
- function page(result) {
30
- return { items: result.items, nextCursor: result.nextCursor ?? null };
31
- }
32
- function relationshipTargets(plan) {
33
- return new Map(plan.entities.map((planned) => [
34
- planned.name,
35
- new Map(planned.traversals.map((traversal) => [traversal.name, traversal.target])),
36
- ]));
37
- }
38
- /** Adds the entities a `where` looks into through relationship filters, at any combinator depth. */
39
- function filterReads(where, targets, reads) {
40
- for (const [key, condition] of Object.entries(where ?? {})) {
41
- if (key === "and" || key === "or") {
42
- for (const part of condition) {
43
- filterReads(part, targets, reads);
44
- }
45
- }
46
- else if (key === "not") {
47
- filterReads(condition, targets, reads);
48
- }
49
- else {
50
- const target = targets.get(key);
51
- if (target !== undefined) {
52
- reads.add(target);
53
- }
54
- }
55
- }
56
- }
57
- /**
58
- * The entities a query on `entity` reads: the entity, the entity of every relationship it includes,
59
- * and the entity of every relationship its filter looks into.
60
- */
61
- function queryReads(entity, query, targets) {
62
- const own = targets.get(entity) ?? new Map();
63
- const reads = new Set([entity]);
64
- for (const name of Object.keys(query.include ?? {})) {
65
- const target = own.get(name);
66
- if (target !== undefined) {
67
- reads.add(target);
68
- }
69
- }
70
- filterReads(query.where, own, reads);
71
- return [...reads];
72
- }
73
- /**
74
- * Requires `<entity>:read` on every entity a request reads. A missing one answers 403 for the whole
75
- * request, before anything is read: nothing is silently left out.
76
- */
77
- function requireRead(entities) {
78
- return Effect.gen(function* () {
79
- const principal = yield* CurrentPrincipal;
80
- const missing = missingPermission(entities.map((entity) => permissionOf(entity, READ_VERB)), principal);
81
- if (missing !== undefined) {
82
- return yield* new PermissionDenied({
83
- permission: missing,
84
- message: `Reading requires ${missing}, which the principal's roles do not grant.`,
85
- });
86
- }
87
- });
88
- }
89
- /**
90
- * Follows a relationship from one record with the query language: the record's own
91
- * `<relationship>_id` for a `rel.one`, and for everything to-many a relationship path from the
92
- * target back to the record (`lead.notes` is notes whose `lead` is the lead, `lead.tags` is tags
93
- * some of whose `leads` is the lead), combined with the caller's query and paged like any find.
94
- */
95
- function traverse(planned, traversal, request, targets) {
96
- return Effect.gen(function* () {
97
- const backend = yield* OntologyBackend;
98
- const { id } = request.params;
99
- const query = toQuery(request.query);
100
- yield* requireRead([planned.name, ...queryReads(traversal.target, query, targets)]);
101
- const source = yield* backend.get({ entity: planned.name, id });
102
- if (traversal.kind === "one") {
103
- const targetId = source[`${traversal.name}_id`];
104
- if (typeof targetId !== "string") {
105
- return { items: [], nextCursor: null };
106
- }
107
- return page(yield* backend.find({
108
- entity: traversal.target,
109
- query: { ...query, where: { id: { eq: targetId } }, limit: 1 },
110
- }));
111
- }
112
- const match = { id: { eq: id } };
113
- const related = traversal.kind === "inverseOfOne"
114
- ? { [traversal.opposite]: match }
115
- : { [traversal.opposite]: { some: match } };
116
- const where = query.where === undefined ? related : { and: [related, query.where] };
117
- return page(yield* backend.find({ entity: traversal.target, query: { ...query, where } }));
118
- });
119
- }
120
20
  function isJsonMediaType(contentType) {
121
21
  if (contentType === undefined || contentType.trim() === "") {
122
22
  return true;
@@ -141,68 +41,7 @@ function readEnvelope(request) {
141
41
  return yield* Schema.decodeUnknownEffect(ActionEnvelope)(body, { errors: "all" }).pipe(Effect.mapError((error) => new InvalidRequestBody({ reason: "invalid_request", issues: schemaIssues(error.issue) })));
142
42
  });
143
43
  }
144
- /**
145
- * The configured hook as `runAction` runs it after the commit. A failure is logged with the request
146
- * id here, so the action's caller still gets the committed result.
147
- */
148
- function afterCommitOf(hook, request, ontology) {
149
- return (committed) => {
150
- const definition = ontology.config.entities.find((entity) => nameToWire(entity.name) === committed.entity)?.definition;
151
- return Effect.tryPromise(async () => hook({
152
- entity: toCamelCase(committed.entity),
153
- action: toCamelCase(committed.action),
154
- principal: principalFromWire(committed.principal),
155
- input: request.generated ? recordFromWire(committed.input, definition) : committed.input,
156
- result: request.generated
157
- ? generatedResultFromWire(committed.result, committed.action, definition)
158
- : committed.result,
159
- edits: editsFromWire(committed.edits, ontology.config.entities),
160
- requestId: request.requestId,
161
- })).pipe(Effect.catchCause((cause) => Effect.logError("ontology.after_commit.failed", cause).pipe(Effect.annotateLogs({
162
- requestId: request.requestId,
163
- action: `${committed.entity}.${committed.action}`,
164
- }))));
165
- };
166
- }
167
- /** The deployment's OIDC token: Vercel sets it on each request, and in the environment of builds. */
168
- function identityToken(request) {
169
- const header = request.headers[OIDC_TOKEN_HEADER];
170
- return typeof header === "string" && header !== "" ? header : process.env.VERCEL_OIDC_TOKEN;
171
- }
172
- function runAction(planned, { name, generated }, ontology, hook, connections) {
173
- return ({ request }) => Effect.gen(function* () {
174
- const backend = yield* OntologyBackend;
175
- const principal = yield* CurrentPrincipal;
176
- const requestId = yield* RequestId;
177
- const envelope = yield* readEnvelope(request);
178
- const validateOnly = envelope.options?.validateOnly === true;
179
- const outcome = yield* backend.runAction({
180
- entity: planned.name,
181
- action: name,
182
- input: envelope.input,
183
- principal,
184
- options: { validateOnly },
185
- requestId,
186
- connections: connections({
187
- identityToken: identityToken(request),
188
- audit: { action: `${planned.name}.${name}`, principalId: principal.id, requestId },
189
- }),
190
- ...(hook === undefined
191
- ? {}
192
- : { afterCommit: afterCommitOf(hook, { requestId, generated }, ontology) }),
193
- });
194
- const { issues } = outcome.validation;
195
- if (!outcome.validation.ok && !validateOnly) {
196
- return yield* new InvalidActionInput({ issues });
197
- }
198
- return {
199
- result: outcome.result ?? null,
200
- validation: { ok: outcome.validation.ok, issues },
201
- edits: outcome.edits,
202
- };
203
- });
204
- }
205
- function entityHandlers(built, planned, hooks, targets, connections) {
44
+ function entityHandlers(built, planned, operationsOf) {
206
45
  return dynamicGroup(built.api, planned.name, (handlers) => Effect.gen(function* () {
207
46
  if (planned.resolved.entity.definition.source !== undefined) {
208
47
  return handlers.handleRaw("sourceCollection", () => requireRead([planned.name]).pipe(Effect.andThen(() => HttpServerResponse.json({
@@ -211,46 +50,38 @@ function entityHandlers(built, planned, hooks, targets, connections) {
211
50
  }, { status: 404 }))));
212
51
  }
213
52
  const backend = yield* OntologyBackend;
214
- // Every read checks the caller's permissions first; the backend is called only afterwards.
215
- const find = (query) => requireRead(queryReads(planned.name, query, targets)).pipe(Effect.andThen(() => backend.find({ entity: planned.name, query })), Effect.map(page));
216
- const readOwn = requireRead([planned.name]);
53
+ const operations = operationsOf(backend);
217
54
  let registered = handlers
218
- .handle("find", (request) => find(toQuery(request.query)))
219
- .handle("query", (request) => find(toQuery(request.payload)))
220
- .handle("get", (request) => readOwn.pipe(Effect.andThen(() => backend.get({ entity: planned.name, id: request.params.id }))))
221
- .handle("history", (request) => readOwn.pipe(Effect.andThen(() => backend.history({ entity: planned.name, id: request.params.id, ...request.query })), Effect.map(page)));
55
+ .handle("find", (request) => operations.find(planned, toQuery(request.query)))
56
+ .handle("query", (request) => operations.find(planned, toQuery(request.payload)))
57
+ .handle("get", (request) => operations.get(planned, request.params.id))
58
+ .handle("history", (request) => operations.history(planned, request.params.id, request.query));
59
+ for (const property of planned.resolved.properties.filter((property) => property.definition.type === "blob")) {
60
+ registered = registered.handle(`blobs.${property.name}`, (request) => Effect.gen(function* () {
61
+ const principal = yield* CurrentPrincipal;
62
+ if (!backend.blobs) {
63
+ return yield* new Conflict({
64
+ entity: planned.name,
65
+ message: "File storage is not configured.",
66
+ });
67
+ }
68
+ return yield* backend.blobs.download(planned.name, request.params.id, property.name, request.payload, principal);
69
+ }));
70
+ }
222
71
  for (const traversal of planned.traversals) {
223
- registered = registered.handle(`related.${traversal.name}`, (request) => traverse(planned, traversal, request, targets).pipe(Effect.provideService(OntologyBackend, backend)));
72
+ registered = registered.handle(`related.${traversal.name}`, (request) => operations.traverse(planned, traversal, request.params.id, toQuery(request.query)));
224
73
  }
225
74
  for (const action of planned.actions) {
226
- const handler = runAction(planned, action, built.plan.ontology, hooks.get(`${planned.name}.${action.name}`), connections);
227
- registered = registered.handleRaw(`actions.${action.name}`, (request) => handler(request).pipe(Effect.provideService(OntologyBackend, backend)));
75
+ registered = registered.handleRaw(`actions.${action.name}`, ({ request }) => readEnvelope(request).pipe(Effect.flatMap((envelope) => operations.runAction(planned, action, {
76
+ input: envelope.input,
77
+ validateOnly: envelope.options?.validateOnly === true,
78
+ ...(envelope.options === undefined ? {} : { options: envelope.options }),
79
+ identityToken: identityToken(request),
80
+ }))));
228
81
  }
229
82
  return registered;
230
83
  }));
231
84
  }
232
- /**
233
- * The caller, as `GET /api/ontology/me` answers: their user record and the roles and permissions
234
- * authentication resolved. It needs no permission: anyone may learn who they are and what they may do.
235
- */
236
- function callerOf(backend) {
237
- return Effect.gen(function* () {
238
- const principal = yield* CurrentPrincipal;
239
- // Authentication resolved the principal from this user a moment ago; its absence is a defect.
240
- const user = yield* backend.get({ entity: "user", id: principal.id }).pipe(Effect.orDie);
241
- return {
242
- user: {
243
- id: principal.id,
244
- name: String(user.name),
245
- email: typeof user.email === "string" ? user.email : null,
246
- kind: principal.kind,
247
- active: user.active === true,
248
- },
249
- roles: principal.roles,
250
- permissions: principal.permissions,
251
- };
252
- });
253
- }
254
85
  /**
255
86
  * Sign-in checks one client may make per minute. A person checks once per sign-in, a few times at
256
87
  * most; the limit slows down anyone testing addresses to learn who the app's members are.
@@ -277,7 +108,7 @@ function checkSignIn(backend, limiter) {
277
108
  return yield* backend.checkSignIn(email);
278
109
  });
279
110
  }
280
- function serviceHandlers(built) {
111
+ function serviceHandlers(built, operationsOf) {
281
112
  const { manifest } = built.plan.ontology;
282
113
  const graph = graphOf(manifest);
283
114
  const signInLimiter = fixedWindowLimiter(SIGN_IN_CHECK_LIMIT);
@@ -295,7 +126,7 @@ function serviceHandlers(built) {
295
126
  return handlers
296
127
  .handle("catalog", (request) => Effect.map(permissionsFor(request.query.as), (permissions) => catalogFor(manifest, permissions)))
297
128
  .handle("graph", () => Effect.succeed(graph))
298
- .handle("me", () => callerOf(backend))
129
+ .handle("me", () => operationsOf(backend).caller)
299
130
  .handle("openapi", () => Effect.sync(() => {
300
131
  if (openApi === undefined) {
301
132
  openApi = OpenApi.fromApi(built.api);
@@ -324,12 +155,37 @@ function serviceHandlers(built) {
324
155
  .handle("signInCheck", checkSignIn(backend, signInLimiter));
325
156
  }));
326
157
  }
158
+ function storageHandlers(built) {
159
+ return dynamicGroup(built.api, STORAGE_GROUP, (handlers) => Effect.gen(function* () {
160
+ const backend = yield* OntologyBackend;
161
+ const configured = () => backend.blobs
162
+ ? Effect.succeed(backend.blobs)
163
+ : Effect.fail(new Conflict({ entity: "upload", message: "File storage is not configured." }));
164
+ let registered = handlers.handle("begin", (request) => Effect.gen(function* () {
165
+ const principal = yield* CurrentPrincipal;
166
+ const blobs = yield* configured();
167
+ return yield* blobs.begin(request.payload, principal);
168
+ }));
169
+ for (const method of ["status", "part", "complete", "cancel"]) {
170
+ registered = registered.handle(method, (request) => Effect.gen(function* () {
171
+ const principal = yield* CurrentPrincipal;
172
+ const blobs = yield* configured();
173
+ return yield* blobs[method](request.params.id, principal);
174
+ }));
175
+ }
176
+ return registered.handle("maintain", () => backend.maintainBlobs ??
177
+ Effect.fail(new Conflict({ entity: "upload", message: "File storage is not configured." })));
178
+ }));
179
+ }
327
180
  /** The handlers of every group of the API; they require {@link OntologyBackend}. */
328
181
  export function handlersLayer(built, hooks, connections) {
329
- const targets = relationshipTargets(built.plan);
182
+ const operationsOf = (backend) => makeOperations(backend, built.plan, hooks, connections);
330
183
  const layers = [
331
- serviceHandlers(built),
332
- ...built.plan.entities.map((planned) => entityHandlers(built, planned, hooks, targets, connections)),
184
+ serviceHandlers(built, operationsOf),
185
+ ...(built.plan.ontology.manifest.entities.some((entity) => entity.properties.some((property) => property.type === "blob"))
186
+ ? [storageHandlers(built)]
187
+ : []),
188
+ ...built.plan.entities.map((planned) => entityHandlers(built, planned, operationsOf)),
333
189
  ];
334
190
  const [first, ...rest] = layers;
335
191
  return Layer.mergeAll(first, ...rest);
@@ -0,0 +1,24 @@
1
+ import { Layer } from "effect";
2
+ import { HttpRouter } from "effect/unstable/http";
3
+ import type { BuiltApi } from "../api.js";
4
+ import { type TokenVerifier } from "../authentication.js";
5
+ import type { AfterCommitHook } from "../config.js";
6
+ import type { ConnectionTransportFactory } from "../connections.js";
7
+ import { RequestId } from "../errors.js";
8
+ import { OntologyBackend } from "../runtime-contract.js";
9
+ /** The URL path of the ontology's MCP server, outside `/api/ontology`: one per app. */
10
+ export declare const MCP_PATH = "/mcp";
11
+ /** The MCP revisions the server speaks, newest first. */
12
+ export declare const MCP_PROTOCOL_VERSIONS: readonly ["2025-11-25", "2025-06-18"];
13
+ export interface McpServerOptions {
14
+ readonly built: BuiltApi;
15
+ readonly hooks: ReadonlyMap<string, AfterCommitHook>;
16
+ readonly connections: ConnectionTransportFactory;
17
+ readonly verifyToken: TokenVerifier;
18
+ }
19
+ /**
20
+ * The ontology's MCP server at `POST /mcp`: Streamable HTTP without sessions, so any instance can
21
+ * answer any request. Its tools run the same operations as the HTTP routes, behind the same
22
+ * authentication. `GET` and `DELETE`, which open a server stream and end a session, answer 405.
23
+ */
24
+ export declare function mcpLayer(options: McpServerOptions): Layer.Layer<never, never, OntologyBackend | HttpRouter.HttpRouter | HttpRouter.Request<"Requires", RequestId>>;