ankka 0.12.0 → 0.13.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 (55) hide show
  1. package/dist/_proto/ankka/protocol/v1/client_pb.d.ts +594 -0
  2. package/dist/_proto/ankka/protocol/v1/client_pb.js +172 -30
  3. package/dist/_proto/ankka/protocol/v1/discovery_pb.d.ts +134 -0
  4. package/dist/_proto/ankka/protocol/v1/discovery_pb.js +51 -36
  5. package/dist/_proto/ankka/protocol/v1/mcp_pb.d.ts +120 -0
  6. package/dist/_proto/ankka/protocol/v1/mcp_pb.js +29 -0
  7. package/dist/agent.d.ts +10 -0
  8. package/dist/agent.js +20 -0
  9. package/dist/autonomous.d.ts +5 -0
  10. package/dist/autonomous.js +10 -0
  11. package/dist/client-keys.d.ts +89 -0
  12. package/dist/client-keys.js +182 -0
  13. package/dist/consumer.d.ts +5 -0
  14. package/dist/consumer.js +10 -0
  15. package/dist/endpoint.d.ts +10 -0
  16. package/dist/endpoint.js +20 -0
  17. package/dist/graph.d.ts +5 -0
  18. package/dist/graph.js +10 -0
  19. package/dist/index.d.ts +6 -1
  20. package/dist/index.js +5 -0
  21. package/dist/mcp-endpoint.d.ts +144 -0
  22. package/dist/mcp-endpoint.js +256 -0
  23. package/dist/routes.d.ts +25 -2
  24. package/dist/routes.js +29 -2
  25. package/dist/server/discovery.d.ts +7 -0
  26. package/dist/server/discovery.js +26 -2
  27. package/dist/server/http.d.ts +8 -0
  28. package/dist/server/http.js +10 -4
  29. package/dist/server/mcp.d.ts +17 -0
  30. package/dist/server/mcp.js +157 -0
  31. package/dist/server/server.d.ts +11 -1
  32. package/dist/server/server.js +19 -0
  33. package/dist/server/storageEndpoint.d.ts +14 -0
  34. package/dist/server/storageEndpoint.js +82 -0
  35. package/dist/service.d.ts +6 -1
  36. package/dist/service.js +47 -4
  37. package/dist/sigv4.d.ts +47 -0
  38. package/dist/sigv4.js +149 -0
  39. package/dist/spec.d.ts +8 -2
  40. package/dist/spec.js +53 -3
  41. package/dist/storage.d.ts +288 -0
  42. package/dist/storage.js +1081 -0
  43. package/dist/testkit/integration.d.ts +9 -0
  44. package/dist/testkit/integration.js +5 -1
  45. package/dist/testkit/kinds.d.ts +16 -4
  46. package/dist/testkit/kinds.js +36 -14
  47. package/dist/testkit/unit.d.ts +4 -1
  48. package/dist/testkit/unit.js +8 -4
  49. package/dist/timedAction.d.ts +10 -0
  50. package/dist/timedAction.js +20 -0
  51. package/dist/version.d.ts +1 -1
  52. package/dist/version.js +1 -1
  53. package/dist/workflow.d.ts +8 -0
  54. package/dist/workflow.js +15 -0
  55. package/package.json +1 -1
@@ -0,0 +1,182 @@
1
+ // A service's client keys (protocol 1.20): credentials it issues for its MCP endpoint, each for one
2
+ // subject, with an expiry, an optional scope of tools and attributes a tool reads as the principal's
3
+ // claims. The runtime keeps them; this process sees a key once, in the answer to `issue`, and never
4
+ // again. Offered to endpoints, MCP endpoints, timed actions and agents, as the secret store is.
5
+ //
6
+ // const issued = await this.clientKeys.issue({ subject: "acme", expiry: Expiry.after({ days: 365 }), scope: ["list_jobs"] })
7
+ // issued.clientKey // shown here, and nowhere again
8
+ import { Code, ConnectError } from "@connectrpc/connect";
9
+ import { channelOf } from "./client.js";
10
+ import { metadataToProto } from "./context.js";
11
+ import { CommandError, ErrorCode } from "./effects/common.js";
12
+ import { errorCodeFromProto } from "./kinds.js";
13
+ import { PROTOCOL_VERSION } from "./spec.js";
14
+ import { Duration, Instant } from "./time.js";
15
+ /** The first protocol whose runtime keeps client keys; an older one answers `UNIMPLEMENTED`. */
16
+ export const CLIENT_KEYS_SINCE = "1.20";
17
+ export const Expiry = Object.freeze({
18
+ /** After a while from now: `Expiry.after({ days: 365 })`, or a `Duration`. */
19
+ after(by) {
20
+ const millis = by instanceof Duration
21
+ ? by.toMillis()
22
+ : (by.days ?? 0) * 86_400_000 + (by.hours ?? 0) * 3_600_000 + (by.minutes ?? 0) * 60_000 + (by.seconds ?? 0) * 1_000 + (by.millis ?? 0);
23
+ if (!Number.isFinite(millis) || millis <= 0)
24
+ throw new TypeError(`a client key's expiry is a positive while, not ${millis}ms`);
25
+ return Object.freeze({ kind: "after", millis });
26
+ },
27
+ /** At an instant. */
28
+ at(when) {
29
+ const epochMillis = when instanceof Instant ? when.toEpochMilli() : when.getTime();
30
+ return Object.freeze({ kind: "at", epochMillis });
31
+ },
32
+ /** Never: admitted until it is revoked. */
33
+ never: Object.freeze({ kind: "never" }),
34
+ });
35
+ function refusalOf(error) {
36
+ return new CommandError({ message: error.message, code: errorCodeFromProto(error.code), details: { ...(error.details ?? {}) } });
37
+ }
38
+ function tooOld(failure) {
39
+ if (failure instanceof ConnectError && failure.code === Code.Unimplemented) {
40
+ throw new CommandError({
41
+ message: `the runtime beside this process is too old for client keys, which needs protocol ${CLIENT_KEYS_SINCE}; this process speaks ${PROTOCOL_VERSION}: ${failure.rawMessage}`,
42
+ code: ErrorCode.Internal,
43
+ });
44
+ }
45
+ throw failure;
46
+ }
47
+ function instant(millis) {
48
+ return millis === undefined ? null : Instant.ofEpochMilli(Number(millis));
49
+ }
50
+ /**
51
+ * The service's client keys, kept by the runtime. A refusal is a `CommandError` with the runtime's
52
+ * code: `BAD_REQUEST` for a request that breaks a rule, `UNAVAILABLE` when the service's MCP
53
+ * endpoints admit no client key (there is then nothing to keep them), `NOT_FOUND` for an unknown id.
54
+ */
55
+ export class ClientKeys {
56
+ #client;
57
+ /** The keys reached through `client`'s sidecar; with none, every call throws. */
58
+ constructor(client) {
59
+ this.#client = client;
60
+ }
61
+ #channel() {
62
+ const channel = this.#client ? channelOf(this.#client) : undefined;
63
+ if (!channel)
64
+ throw new Error("these client keys are not connected to a runtime; a unit test passes an InMemoryClientKeys");
65
+ return channel;
66
+ }
67
+ /** Issues a key; the answer is the one place the key is ever shown. */
68
+ async issue(request) {
69
+ const { stub, metadata } = this.#channel();
70
+ const expiry = request.expiry;
71
+ const reply = await stub()
72
+ .issueClientKey({
73
+ subject: request.subject,
74
+ expiry: expiry.kind === "after"
75
+ ? { case: "afterMillis", value: BigInt(expiry.millis) }
76
+ : expiry.kind === "at"
77
+ ? { case: "atEpochMillis", value: BigInt(expiry.epochMillis) }
78
+ : { case: "never", value: true },
79
+ ...(request.endpoint !== undefined ? { endpoint: request.endpoint } : {}),
80
+ scope: [...(request.scope ?? [])],
81
+ attributes: { ...(request.attributes ?? {}) },
82
+ ...(request.label !== undefined ? { label: request.label } : {}),
83
+ metadata: metadataToProto(metadata),
84
+ })
85
+ .catch(tooOld);
86
+ switch (reply.reply.case) {
87
+ case "issued":
88
+ return Object.freeze({ id: reply.reply.value.id, clientKey: reply.reply.value.clientKey });
89
+ case "error":
90
+ throw refusalOf(reply.reply.value);
91
+ default:
92
+ throw new CommandError({ message: "the runtime answered an issue with nothing", code: ErrorCode.Internal });
93
+ }
94
+ }
95
+ /** Every client key of the service, revoked ones included; never a key itself. */
96
+ async list() {
97
+ const { stub, metadata } = this.#channel();
98
+ const reply = await stub().listClientKeys({ metadata: metadataToProto(metadata) }).catch(tooOld);
99
+ if (reply.error)
100
+ throw refusalOf(reply.error);
101
+ return reply.keys.map((k) => Object.freeze({
102
+ id: k.id,
103
+ subject: k.subject,
104
+ attributes: Object.freeze({ ...k.attributes }),
105
+ label: k.label ?? null,
106
+ endpoint: k.endpoint,
107
+ scope: Object.freeze([...k.scope]),
108
+ issuedAt: Instant.ofEpochMilli(Number(k.issuedAtMillis)),
109
+ expiresAt: instant(k.expiresAtMillis),
110
+ lastUsed: instant(k.lastUsedMillis),
111
+ revokedAt: instant(k.revokedAtMillis),
112
+ }));
113
+ }
114
+ /** Revokes a key: refused on its next request on every instance. Revoking twice is not an error. */
115
+ async revoke(id) {
116
+ const { stub, metadata } = this.#channel();
117
+ const reply = await stub().revokeClientKey({ id, metadata: metadataToProto(metadata) }).catch(tooOld);
118
+ if (reply.error)
119
+ throw refusalOf(reply.error);
120
+ }
121
+ }
122
+ /** Client keys for unit tests: a map, issuing keys that look like the runtime's and applying its rules. */
123
+ export class InMemoryClientKeys extends ClientKeys {
124
+ #keys = new Map();
125
+ #next = 0;
126
+ constructor() {
127
+ super(undefined);
128
+ }
129
+ /** The key issued under `id`, which a test may present; the runtime shows it only once. */
130
+ keyOf(id) {
131
+ return this.#keys.get(id)?.clientKey;
132
+ }
133
+ async issue(request) {
134
+ if (typeof request.subject !== "string" || request.subject.trim() === "") {
135
+ throw new CommandError({ message: "a client key needs a subject", code: ErrorCode.BadRequest });
136
+ }
137
+ const attributes = request.attributes ?? {};
138
+ if (Object.keys(attributes).length > 16)
139
+ throw new CommandError({ message: "a client key carries at most 16 attributes", code: ErrorCode.BadRequest });
140
+ for (const [name, value] of Object.entries(attributes)) {
141
+ if (name.length > 256 || value.length > 256)
142
+ throw new CommandError({ message: `attribute '${name.slice(0, 40)}' is longer than 256 characters`, code: ErrorCode.BadRequest });
143
+ }
144
+ this.#next += 1;
145
+ const id = `ck_${this.#next}`;
146
+ const clientKey = `ak_test_${id}_${Math.random().toString(36).slice(2)}`;
147
+ const now = Instant.now();
148
+ const expiry = request.expiry;
149
+ const expiresAt = expiry.kind === "after" ? Instant.ofEpochMilli(now.toEpochMilli() + expiry.millis) : expiry.kind === "at" ? Instant.ofEpochMilli(expiry.epochMillis) : null;
150
+ const summary = Object.freeze({
151
+ id,
152
+ subject: request.subject,
153
+ attributes: Object.freeze({ ...attributes }),
154
+ label: request.label ?? null,
155
+ endpoint: request.endpoint ?? "",
156
+ scope: Object.freeze([...(request.scope ?? [])]),
157
+ issuedAt: now,
158
+ expiresAt,
159
+ lastUsed: null,
160
+ revokedAt: null,
161
+ });
162
+ this.#keys.set(id, { summary, clientKey });
163
+ return Object.freeze({ id, clientKey });
164
+ }
165
+ async list() {
166
+ return [...this.#keys.values()].map((k) => k.summary);
167
+ }
168
+ async revoke(id) {
169
+ const held = this.#keys.get(id);
170
+ if (!held)
171
+ throw new CommandError({ message: `no client key '${id}'`, code: ErrorCode.NotFound });
172
+ if (held.summary.revokedAt === null)
173
+ this.#keys.set(id, { ...held, summary: Object.freeze({ ...held.summary, revokedAt: Instant.now() }) });
174
+ }
175
+ }
176
+ /**
177
+ * @internal The client keys reached through `client`'s sidecar. Not a member of the client, which an
178
+ * entity holds too: only the component kinds that may issue keys call this.
179
+ */
180
+ export function clientKeysFor(client) {
181
+ return new ClientKeys(client);
182
+ }
@@ -3,6 +3,7 @@ import type { Contract, Publication } from "./contract.ts";
3
3
  import type { Shape } from "./codec.ts";
4
4
  import type { Metadata } from "./effects/common.ts";
5
5
  import { type ComponentClient, type ComponentRef, type Secrets } from "./client.ts";
6
+ import { type Buckets } from "./storage.ts";
6
7
  import { ConsumerEffects, type ConsumerEffect } from "./effects/stateless.ts";
7
8
  import { type Services } from "./services.ts";
8
9
  import type { Standing } from "./standing.ts";
@@ -40,6 +41,10 @@ export declare abstract class Consumer<M, Out = never> {
40
41
  get secrets(): Secrets;
41
42
  /** A unit test's store in place of the runtime's: `component.secrets = new InMemorySecrets()`. */
42
43
  set secrets(store: Secrets);
44
+ /** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
45
+ get storage(): Buckets;
46
+ /** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
47
+ set storage(buckets: Buckets);
43
48
  /** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
44
49
  get services(): Services;
45
50
  /** A unit test's in place of the runtime's: `component.services = new ScriptedServices()`. */
package/dist/consumer.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // A consumer: reacts to a source's changes, optionally producing onward to a topic.
2
2
  import { secretsFor } from "./client.js";
3
+ import { processStorage } from "./storage.js";
3
4
  import { ConsumerEffects } from "./effects/stateless.js";
4
5
  import { servicesFor } from "./services.js";
5
6
  /** The metadata entry a runtime sets on a consumer's request to say what it speaks: its protocol version. */
@@ -70,6 +71,15 @@ export class Consumer {
70
71
  set secrets(store) {
71
72
  this.#secrets = store;
72
73
  }
74
+ #storage;
75
+ /** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
76
+ get storage() {
77
+ return this.#storage ?? processStorage();
78
+ }
79
+ /** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
80
+ set storage(buckets) {
81
+ this.#storage = buckets;
82
+ }
73
83
  #services;
74
84
  /** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
75
85
  get services() {
@@ -1,4 +1,6 @@
1
+ import { type ClientKeys } from "./client-keys.ts";
1
2
  import { type ComponentClient, type Secrets } from "./client.ts";
3
+ import { type Buckets } from "./storage.ts";
2
4
  import { type RequestContext } from "./context.ts";
3
5
  import type { Acl, RouteTable } from "./routes.ts";
4
6
  import { type Services } from "./services.ts";
@@ -12,10 +14,18 @@ export declare abstract class Endpoint {
12
14
  get secrets(): Secrets;
13
15
  /** A unit test's store in place of the runtime's: `component.secrets = new InMemorySecrets()`. */
14
16
  set secrets(store: Secrets);
17
+ /** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
18
+ get storage(): Buckets;
19
+ /** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
20
+ set storage(buckets: Buckets);
15
21
  /** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
16
22
  get services(): Services;
17
23
  /** A unit test's in place of the runtime's: `component.services = new ScriptedServices()`. */
18
24
  set services(services: Services);
25
+ /** The service's client keys (1.20): issued for its MCP endpoint, kept by the runtime, each shown once. */
26
+ get clientKeys(): ClientKeys;
27
+ /** A unit test's in place of the runtime's: `component.clientKeys = new InMemoryClientKeys()`. */
28
+ set clientKeys(keys: ClientKeys);
19
29
  /** @internal */
20
30
  _bindClient(client: ComponentClient | undefined): void;
21
31
  }
package/dist/endpoint.js CHANGED
@@ -8,7 +8,9 @@
8
8
  // static readonly routes = { getCart: get("/{cartId}", ShoppingCart, (ep: ShoppingCartEndpoint, req) => ep.getCart(req.params.cartId)) }
9
9
  // getCart(cartId: string) { return this.client.of(ShoppingCartEntity, cartId).call(ShoppingCartEntity.handlers.getCart).invoke() }
10
10
  // }
11
+ import { clientKeysFor } from "./client-keys.js";
11
12
  import { secretsFor } from "./client.js";
13
+ import { processStorage } from "./storage.js";
12
14
  import { currentRequest, requestIfAny } from "./context.js";
13
15
  import { servicesFor } from "./services.js";
14
16
  export class Endpoint {
@@ -33,6 +35,15 @@ export class Endpoint {
33
35
  set secrets(store) {
34
36
  this.#secrets = store;
35
37
  }
38
+ #storage;
39
+ /** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
40
+ get storage() {
41
+ return this.#storage ?? processStorage();
42
+ }
43
+ /** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
44
+ set storage(buckets) {
45
+ this.#storage = buckets;
46
+ }
36
47
  #services;
37
48
  /** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
38
49
  get services() {
@@ -42,6 +53,15 @@ export class Endpoint {
42
53
  set services(services) {
43
54
  this.#services = services;
44
55
  }
56
+ #clientKeys;
57
+ /** The service's client keys (1.20): issued for its MCP endpoint, kept by the runtime, each shown once. */
58
+ get clientKeys() {
59
+ return this.#clientKeys ?? clientKeysFor(this.client);
60
+ }
61
+ /** A unit test's in place of the runtime's: `component.clientKeys = new InMemoryClientKeys()`. */
62
+ set clientKeys(keys) {
63
+ this.#clientKeys = keys;
64
+ }
45
65
  /** @internal */
46
66
  _bindClient(client) {
47
67
  this.#client = client;
package/dist/graph.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { StartFrom } from "./startFrom.ts";
2
2
  import { type Codec, type Shape } from "./codec.ts";
3
3
  import type { EffectLike, Metadata } from "./effects/common.ts";
4
4
  import { type ComponentClient, type ComponentRef, type Secrets } from "./client.ts";
5
+ import { type Buckets } from "./storage.ts";
5
6
  import { type Services } from "./services.ts";
6
7
  import type { Standing } from "./standing.ts";
7
8
  type MaybePromise<T> = T | Promise<T>;
@@ -146,6 +147,10 @@ export declare abstract class GraphConsumer<M> {
146
147
  get secrets(): Secrets;
147
148
  /** A unit test's store in place of the runtime's: `component.secrets = new InMemorySecrets()`. */
148
149
  set secrets(store: Secrets);
150
+ /** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
151
+ get storage(): Buckets;
152
+ /** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
153
+ set storage(buckets: Buckets);
149
154
  /** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
150
155
  get services(): Services;
151
156
  /** A unit test's in place of the runtime's: `component.services = new ScriptedServices()`. */
package/dist/graph.js CHANGED
@@ -16,6 +16,7 @@
16
16
  // }
17
17
  import { JSON_CONTENT } from "./codec.js";
18
18
  import { secretsFor } from "./client.js";
19
+ import { processStorage } from "./storage.js";
19
20
  import { sequenceNumberOf } from "./consumer.js";
20
21
  import { renderDouble, reviver } from "./json.js";
21
22
  import { servicesFor } from "./services.js";
@@ -426,6 +427,15 @@ export class GraphConsumer {
426
427
  set secrets(store) {
427
428
  this.#secrets = store;
428
429
  }
430
+ #storage;
431
+ /** The service's buckets: `this.storage.default`, or `this.storage.bucket("photos")`. */
432
+ get storage() {
433
+ return this.#storage ?? processStorage();
434
+ }
435
+ /** A unit test's store in place of the runtime's: `component.storage = new InMemoryBuckets()`. */
436
+ set storage(buckets) {
437
+ this.#storage = buckets;
438
+ }
429
439
  #services;
430
440
  /** Other services, called as this one, through the runtime: `this.services.service("orders")`. */
431
441
  get services() {
package/dist/index.d.ts CHANGED
@@ -25,14 +25,19 @@ export { ApprovalAwaited, APPROVALS_SINCE, type AgentOutcome, type Approval, typ
25
25
  export { mcpServer, resultGuardrail, toolName, type McpServerOptions, type McpServerRef, type ResultGuardrailRef } from "./mcp.ts";
26
26
  export { AutonomousAgent, AutonomousAgentCalls, TaskCalls, Tasks, accepted, rejected, taskAcceptance, taskRule, taskType, type Accepted, type AgentState, type Attachment, type AutonomousAgentClass, type AutonomousSettings, type Notification, type Rejected, type TaskAcceptance, type TaskRule, type TaskSnapshot, type TaskType, } from "./autonomous.ts";
27
27
  export { Endpoint, type EndpointClass } from "./endpoint.ts";
28
+ export { McpEndpoint, ToolTable, tools, MCP_ENDPOINTS_SINCE, type McpEndpointClass, type DeclaredTool, type ToolOptions, type ReferenceOptions, type RegisteredMcpEndpoint, type RegisteredMcpTool, type ToolRun, } from "./mcp-endpoint.ts";
29
+ export { ClientKeys, InMemoryClientKeys, Expiry, CLIENT_KEYS_SINCE, type IssueClientKey, type IssuedClientKey, type ClientKeySummary, } from "./client-keys.ts";
28
30
  export { type Socket, SocketClosed } from "./socket.ts";
29
- export { Acl, Callers, type CallerMatcher, type CallersAcl, HttpProblem, get, post, put, patch, del, sse, sseEvent, sseEvents, type SseEvent, socket, type RouteRef, type RouteOptions, type RouteTable, type Params, type ParamNames, type HttpMethod } from "./routes.ts";
31
+ export { Acl, Callers, type CallerMatcher, type CallersAcl, type AnyOfAcl, HttpProblem, get, post, put, patch, del, sse, sseEvent, sseEvents, type SseEvent, socket, type RouteRef, type RouteOptions, type RouteTable, type Params, type ParamNames, type HttpMethod } from "./routes.ts";
30
32
  export { type CommandContext, type EventContext, type RequestContext, type Principal, type Caller, Query, Headers } from "./context.ts";
31
33
  export { ComponentClient, Calls, TypedCalls, Invocation, AwaitingInvocation, AWAIT_SINCE, awaitPartJson, Views, Timers, Secrets, InMemorySecrets, noSecrets, WatchEnded, VIEW_STREAMS_SINCE, type AwaitPart, type WatchEvent, type WatchOptions, type Overflow, type TimerTarget, type TimedActionTarget, type ComponentRef } from "./client.ts";
34
+ export { ObjectRules, StorageRefusals, InMemoryBuckets, DirectoryBuckets, S3Buckets, S3Bucket, noStorage, chooseStorage, declaredBuckets, processStorage, s3Settings, bucketNameOf, bucketVariableSuffix, localAddress, STORAGE_PATH, type Buckets, type Bucket, type BucketExposure, type SignedAccess, type StoredObject, type ObjectInfo, type ObjectEntry, type PutOptions, type InMemoryBucketsOptions, type DirectoryBucketsOptions, type DeclaredBuckets, type S3Settings, type S3Target, type NamedTarget, type StorageBackend, type ChosenStorage, type StorageChoiceOptions, } from "./storage.ts";
35
+ export { Credentials, SignedRequest, sign as signRequest, presign as presignUrl } from "./sigv4.ts";
32
36
  export { Services, ServiceClient, ScriptedServices, noServices, ServiceError, ServiceUnresolvable, ServiceIdentityMismatch, ServiceUnanswered, ServiceCallFailed, type ServiceResponse, type ServiceRequest, type ServiceHeaders, } from "./services.ts";
33
37
  export { Ankka, ServiceBuilder, Registry, RegistrationError, type ServiceOptions, type RegisteredComponent, type RegisteredEndpoint, type Source, type RegisteredEventSourced, type RegisteredKeyValue, type RegisteredWorkflow, type RegisteredView, type RegisteredKeyedView, type RegisteredKeyedSource, type RegisteredConsumer, type RegisteredTimedAction, type RegisteredAgent, } from "./service.ts";
34
38
  export { Server, type ServerOptions } from "./server/server.ts";
35
39
  export { problems as sidecarProblems } from "./server/discovery.ts";
40
+ export { createMcpDispatcher, type McpDispatcher, type ToolAnswer } from "./server/mcp.ts";
36
41
  export { PROTOCOL_VERSION, SDK_NAME } from "./spec.ts";
37
42
  export { StartFrom } from "./startFrom.ts";
38
43
  export { isTerminal, isUnknown, type Standing, type StandingStatus } from "./standing.ts";
package/dist/index.js CHANGED
@@ -36,14 +36,19 @@ export { ApprovalAwaited, APPROVALS_SINCE } from "./approvals.js";
36
36
  export { mcpServer, resultGuardrail, toolName } from "./mcp.js";
37
37
  export { AutonomousAgent, AutonomousAgentCalls, TaskCalls, Tasks, accepted, rejected, taskAcceptance, taskRule, taskType, } from "./autonomous.js";
38
38
  export { Endpoint } from "./endpoint.js";
39
+ export { McpEndpoint, ToolTable, tools, MCP_ENDPOINTS_SINCE, } from "./mcp-endpoint.js";
40
+ export { ClientKeys, InMemoryClientKeys, Expiry, CLIENT_KEYS_SINCE, } from "./client-keys.js";
39
41
  export { SocketClosed } from "./socket.js";
40
42
  export { Acl, Callers, HttpProblem, get, post, put, patch, del, sse, sseEvent, sseEvents, socket } from "./routes.js";
41
43
  export { Query, Headers } from "./context.js";
42
44
  export { ComponentClient, Calls, TypedCalls, Invocation, AwaitingInvocation, AWAIT_SINCE, awaitPartJson, Views, Timers, Secrets, InMemorySecrets, noSecrets, WatchEnded, VIEW_STREAMS_SINCE } from "./client.js";
45
+ export { ObjectRules, StorageRefusals, InMemoryBuckets, DirectoryBuckets, S3Buckets, S3Bucket, noStorage, chooseStorage, declaredBuckets, processStorage, s3Settings, bucketNameOf, bucketVariableSuffix, localAddress, STORAGE_PATH, } from "./storage.js";
46
+ export { Credentials, SignedRequest, sign as signRequest, presign as presignUrl } from "./sigv4.js";
43
47
  export { Services, ServiceClient, ScriptedServices, noServices, ServiceError, ServiceUnresolvable, ServiceIdentityMismatch, ServiceUnanswered, ServiceCallFailed, } from "./services.js";
44
48
  export { Ankka, ServiceBuilder, Registry, RegistrationError, } from "./service.js";
45
49
  export { Server } from "./server/server.js";
46
50
  export { problems as sidecarProblems } from "./server/discovery.js";
51
+ export { createMcpDispatcher } from "./server/mcp.js";
47
52
  export { PROTOCOL_VERSION, SDK_NAME } from "./spec.js";
48
53
  export { StartFrom } from "./startFrom.js";
49
54
  export { isTerminal, isUnknown } from "./standing.js";
@@ -0,0 +1,144 @@
1
+ import { type Codec, type Shape } from "./codec.ts";
2
+ import type { ComponentRef } from "./client.ts";
3
+ import { Endpoint } from "./endpoint.ts";
4
+ import type { HandlerRef } from "./handlers.ts";
5
+ import { type Acl, type HttpMethod, type RouteRef } from "./routes.ts";
6
+ import type { ComponentKind } from "./kinds.ts";
7
+ import type { EndpointClass } from "./endpoint.ts";
8
+ import type { RegisteredComponent, RegisteredEndpoint } from "./service.ts";
9
+ type MaybePromise<T> = T | Promise<T>;
10
+ /** The first protocol whose runtime serves a process's MCP endpoint. */
11
+ export declare const MCP_ENDPOINTS_SINCE = "1.20";
12
+ /** What a tool name may be: what MCP clients accept everywhere. */
13
+ export declare const TOOL_NAME: RegExp;
14
+ /**
15
+ * The base of an MCP endpoint. One instance serves every call, so the call's principal and caller are
16
+ * on `this.request`, as for an HTTP endpoint; `client`, `secrets`, `services` and `clientKeys` likewise.
17
+ */
18
+ export declare abstract class McpEndpoint extends Endpoint {
19
+ }
20
+ /** One tool as declared, before its references are resolved. */
21
+ export type DeclaredTool<Ep> = {
22
+ readonly kind: "tool";
23
+ readonly name: string;
24
+ readonly description: string;
25
+ readonly input: Shape<any>;
26
+ readonly reply: Shape<any> | undefined;
27
+ readonly run: (args: any, self: Ep) => MaybePromise<unknown>;
28
+ readonly acl: Acl | undefined;
29
+ } | {
30
+ readonly kind: "command" | "query";
31
+ readonly name: string;
32
+ readonly description: string;
33
+ readonly component: ComponentRef;
34
+ readonly handler: string;
35
+ /** The argument holding the entity's or workflow's id. */
36
+ readonly id: string;
37
+ /** The argument holding the handler's input; absent merges a record input's fields beside the id. */
38
+ readonly input: string | undefined;
39
+ readonly acl: Acl | undefined;
40
+ } | {
41
+ readonly kind: "route";
42
+ readonly name: string;
43
+ readonly description: string;
44
+ readonly endpoint: EndpointClass<any>;
45
+ readonly method: HttpMethod;
46
+ /** The full path: the endpoint's prefix and the route's template, `/orders/{day}`. */
47
+ readonly path: string;
48
+ readonly acl: Acl | undefined;
49
+ };
50
+ export interface ToolOptions {
51
+ /** Who may call this tool, beside the endpoint's rule; client keys and `anyOf` are allowed. */
52
+ readonly acl?: Acl;
53
+ }
54
+ export interface ReferenceOptions extends ToolOptions {
55
+ /** The argument that names the entity or workflow. */
56
+ readonly id: string;
57
+ /** The argument holding the handler's input, when it is not to be merged beside the id. */
58
+ readonly input?: string;
59
+ }
60
+ /** The tools of an MCP endpoint, built as `tools<CartMcp>().tool(...).command(...)`. */
61
+ export declare class ToolTable<Ep = McpEndpoint> {
62
+ #private;
63
+ readonly declared: readonly DeclaredTool<Ep>[];
64
+ constructor(declared?: readonly DeclaredTool<Ep>[]);
65
+ /**
66
+ * A tool this process runs: its arguments decoded with `input` (a record schema becomes the tool's
67
+ * object schema), its answer encoded with `reply` (a string answers as text; an object also as
68
+ * structured content). A thrown error or a refusal is the tool error the client is shown.
69
+ */
70
+ tool<I, R>(name: string, description: string, input: Shape<I>, reply: Shape<R> | undefined, run: (args: I, self: Ep) => MaybePromise<R>, options?: ToolOptions): ToolTable<Ep>;
71
+ /** A tool that runs a command of a component, by its wire name: `command("checkout", "...", ShoppingCart, "checkout", { id: "cartId" })`. */
72
+ command(name: string, description: string, component: ComponentRef, handler: string | HandlerRef<any, any, any, any>, options: ReferenceOptions): ToolTable<Ep>;
73
+ /** A tool that runs a query of a component; it cannot change anything. */
74
+ query(name: string, description: string, component: ComponentRef, handler: string | HandlerRef<any, any, any, any>, options: ReferenceOptions): ToolTable<Ep>;
75
+ /** A tool that calls a route of one of this service's endpoints, in process: its path names are the arguments, and `body` the body. */
76
+ route(name: string, description: string, endpoint: EndpointClass<any>, method: HttpMethod, path: string, options?: ToolOptions): ToolTable<Ep>;
77
+ }
78
+ /** An empty tool table to build on. */
79
+ export declare function tools<Ep = McpEndpoint>(): ToolTable<Ep>;
80
+ /** The statics an MCP endpoint class declares; `register` constrains on this type. */
81
+ export interface McpEndpointClass<C extends McpEndpoint = McpEndpoint> {
82
+ new (): C;
83
+ /** `/mcp`: where the endpoint is served, on the service's own hostnames. */
84
+ readonly path: string;
85
+ /** Who may connect: `Acl.clientKeys`, `Acl.authenticated`, `Acl.anyOf(...)`. There is no default. */
86
+ readonly acl: Acl;
87
+ readonly tools: ToolTable<C>;
88
+ /** The platform signs people in for clients that cannot hold a token (needs `authenticated` in the rule). */
89
+ readonly brokered?: boolean;
90
+ /** The server's name in `initialize`; the class name when absent. */
91
+ readonly serverName?: string;
92
+ readonly serverVersion?: string;
93
+ }
94
+ /** How a resolved tool is run; data the server interprets. */
95
+ export type ToolRun = {
96
+ readonly kind: "function";
97
+ readonly input: Shape<any>;
98
+ readonly reply: Shape<any> | undefined;
99
+ readonly run: (args: any, self: McpEndpoint) => MaybePromise<unknown>;
100
+ } | {
101
+ readonly kind: "handler";
102
+ readonly componentKind: ComponentKind;
103
+ readonly componentId: string;
104
+ readonly handler: HandlerRef<any, any, any, any>;
105
+ readonly id: string;
106
+ /** `merge`: a record input's fields sit beside the id; a name: the input is that argument; absent: there is none. */
107
+ readonly input: {
108
+ readonly kind: "merge";
109
+ } | {
110
+ readonly kind: "argument";
111
+ readonly name: string;
112
+ } | undefined;
113
+ } | {
114
+ readonly kind: "route";
115
+ readonly endpointId: string;
116
+ readonly route: RouteRef<any, any, any, any>;
117
+ readonly paramNames: readonly string[];
118
+ };
119
+ export interface RegisteredMcpTool {
120
+ readonly name: string;
121
+ readonly description: string;
122
+ /** The JSON Schema of the arguments, which the platform checks before the process is asked. */
123
+ readonly inputSchema: Record<string, unknown>;
124
+ readonly acl: Acl | undefined;
125
+ readonly run: ToolRun;
126
+ }
127
+ export interface RegisteredMcpEndpoint {
128
+ readonly id: string;
129
+ readonly cls: McpEndpointClass<any>;
130
+ readonly path: string;
131
+ readonly acl: Acl;
132
+ readonly tools: readonly RegisteredMcpTool[];
133
+ readonly brokered: boolean;
134
+ readonly serverName: string | undefined;
135
+ readonly serverVersion: string | undefined;
136
+ }
137
+ /**
138
+ * Resolves an MCP endpoint class against the registered components and endpoints, collecting every
139
+ * problem. A reference to a component or route that is not registered is refused here, before start.
140
+ */
141
+ export declare function registerMcpEndpoint(cls: McpEndpointClass<any>, components: ReadonlyMap<string, RegisteredComponent>, endpoints: ReadonlyMap<string, RegisteredEndpoint>, problems: string[]): RegisteredMcpEndpoint | undefined;
142
+ /** The codec a tool's own reply is encoded with, when it declares one. */
143
+ export declare function replyCodecOf(reply: Shape<any> | undefined): Codec<unknown> | undefined;
144
+ export {};