@stagewise/mcp-extension-push-notifications 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +122 -0
- package/dist/capabilities.d.ts +18 -0
- package/dist/capabilities.d.ts.map +1 -0
- package/dist/capabilities.js +47 -0
- package/dist/capabilities.js.map +1 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/register-push-notifications-client.d.ts +25 -0
- package/dist/client/register-push-notifications-client.d.ts.map +1 -0
- package/dist/client/register-push-notifications-client.js +116 -0
- package/dist/client/register-push-notifications-client.js.map +1 -0
- package/dist/constants.d.ts +10 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +10 -0
- package/dist/constants.js.map +1 -0
- package/dist/generated/schema.d.ts +516 -0
- package/dist/generated/schema.d.ts.map +1 -0
- package/dist/generated/schema.js +133 -0
- package/dist/generated/schema.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/server/http-subscriptions.d.ts +16 -0
- package/dist/server/http-subscriptions.d.ts.map +1 -0
- package/dist/server/http-subscriptions.js +235 -0
- package/dist/server/http-subscriptions.js.map +1 -0
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +3 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/register-push-notifications-server.d.ts +32 -0
- package/dist/server/register-push-notifications-server.d.ts.map +1 -0
- package/dist/server/register-push-notifications-server.js +55 -0
- package/dist/server/register-push-notifications-server.js.map +1 -0
- package/dist/spec.types.d.ts +154 -0
- package/dist/spec.types.d.ts.map +1 -0
- package/dist/spec.types.js +2 -0
- package/dist/spec.types.js.map +1 -0
- package/package.json +55 -0
- package/schema.json +2466 -0
- package/specification/draft/events.md +206 -0
package/README.md
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# MCP Push Notifications Extension
|
|
2
|
+
|
|
3
|
+
Typed schemas and client/server facades for identity-scoped durable Push Notifications in the Model Context Protocol.
|
|
4
|
+
|
|
5
|
+
**Extension identifier:** `io.stagewise/push-notifications`
|
|
6
|
+
|
|
7
|
+
## Delivery model
|
|
8
|
+
|
|
9
|
+
- `io.stagewise/push-notifications/get` retrieves pending unacknowledged events for the authenticated consumer.
|
|
10
|
+
- `io.stagewise/push-notifications/ack` records durable client acceptance and removes events from the pending view.
|
|
11
|
+
- `io.stagewise/push-notifications/event` provides optional low-latency delivery.
|
|
12
|
+
|
|
13
|
+
Delivery is at least once. Servers persist before publishing. Clients accept and deduplicate by `eventId` before acknowledging. Live delivery is a latency optimization; pending retrieval is recovery. This extension is a durable queue, not a replayable historical event stream.
|
|
14
|
+
|
|
15
|
+
Every event carries ordered MCP `ContentBlock` values in `content` and may carry
|
|
16
|
+
producer-owned JSON in `data`. The runtime schema is the canonical
|
|
17
|
+
`ContentBlockSchema` from this package's pinned MCP SDK revision, so text, images,
|
|
18
|
+
audio, embedded resources, and resource links use standard MCP representations.
|
|
19
|
+
Transport support does not imply that every client or model can consume every
|
|
20
|
+
content type.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const event = {
|
|
24
|
+
eventId: crypto.randomUUID(),
|
|
25
|
+
sourceId: 'chat:local',
|
|
26
|
+
type: 'chat.message.received',
|
|
27
|
+
createdAt: new Date().toISOString(),
|
|
28
|
+
content: [{ type: 'text', text: 'Hello' }],
|
|
29
|
+
data: { messageId: '42' },
|
|
30
|
+
};
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Small media may be carried inline as base64 MCP image/audio blocks. Larger or
|
|
34
|
+
long-lived media should use MCP resource links whose authorization and retention
|
|
35
|
+
match the event feed.
|
|
36
|
+
|
|
37
|
+
## Client
|
|
38
|
+
|
|
39
|
+
Register before connecting:
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
const events = registerPushNotificationsClient(mcpClient, {
|
|
43
|
+
async onEvent({ params }) {
|
|
44
|
+
const accepted = await inbox.commit([params.event]);
|
|
45
|
+
if (accepted) await events.acknowledgeEvents({ eventIds: [params.event.eventId] });
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Subscribe first, then drain pending pages:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
const subscription = await events.listen();
|
|
54
|
+
|
|
55
|
+
while (true) {
|
|
56
|
+
const page = await events.getEvents({ limit: 100 });
|
|
57
|
+
await inbox.commit(page.events);
|
|
58
|
+
if (page.events.length > 0) {
|
|
59
|
+
await events.acknowledgeEvents({
|
|
60
|
+
eventIds: page.events.map((event) => event.eventId),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (!page.hasMore) break;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
subscription.closed.catch(() => reconnect());
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
A lost acknowledgement can cause the same events to return again. `eventId` is the idempotency key. `listen()` resolves after subscription acknowledgement; `closed` settles when the long-lived request completes or fails.
|
|
70
|
+
|
|
71
|
+
Clients must treat content, MIME declarations, linked resources, and event data as untrusted input.
|
|
72
|
+
|
|
73
|
+
## Server facade
|
|
74
|
+
|
|
75
|
+
Storage and consumer resolution remain application concerns:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const events = registerPushNotificationsServer(mcpServer, {
|
|
79
|
+
getEvents: ({ limit }, context) => store.pending(consumerFrom(context), limit),
|
|
80
|
+
acknowledgeEvents: ({ eventIds }, context) =>
|
|
81
|
+
store.acknowledge(consumerFrom(context), eventIds),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
await store.append(consumerKey, event);
|
|
85
|
+
await events.sendEvent({ event }, { metadata: requestMeta });
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Acknowledgement is idempotent. Acknowledged IDs disappear immediately from pending retrieval. Retention of payloads and acknowledgement tombstones is server policy.
|
|
89
|
+
|
|
90
|
+
## HTTP subscriptions
|
|
91
|
+
|
|
92
|
+
The HTTP manager requires a trusted consumer-key resolver and targeted publication:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
const subscriptions = createPushNotificationsHttpSubscriptionManager(mcp.fetch, {
|
|
96
|
+
resolveConsumerKey: (request) => authenticatedConsumerKey(request),
|
|
97
|
+
onSubscriptionStateChanged: (consumerKey, active) => {
|
|
98
|
+
updateConsumerActivity(consumerKey, active);
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
app.all('/mcp', (context) => subscriptions.fetch(context.req.raw));
|
|
103
|
+
|
|
104
|
+
await store.append(consumerKey, event);
|
|
105
|
+
subscriptions.publish(consumerKey, { event });
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Only one active live subscription is retained per consumer key; a new subscription replaces the previous stream. The key is derived from authentication context and never accepted from a Push Notifications payload. The optional lifecycle callback reports installation and removal of the active stream; observer failures are isolated from protocol handling.
|
|
109
|
+
|
|
110
|
+
## Capabilities and schemas
|
|
111
|
+
|
|
112
|
+
Every facade request declares the client capability. Server support is discovered lazily and cached. `src/spec.types.ts` is the source of truth; generated Zod schemas and `schema.json` must remain fresh.
|
|
113
|
+
|
|
114
|
+
```sh
|
|
115
|
+
pnpm generate:schemas
|
|
116
|
+
pnpm check:schema
|
|
117
|
+
pnpm typecheck
|
|
118
|
+
pnpm test
|
|
119
|
+
pnpm build
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
See `specification/draft/events.md` for the normative contract.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { MISSING_REQUIRED_CLIENT_CAPABILITY_CODE } from './constants.js';
|
|
2
|
+
import type { MissingRequiredClientCapabilityData, PushNotificationsCapabilities, PushNotificationsClientCapabilitiesMeta } from './spec.types.js';
|
|
3
|
+
export type Metadata = Record<string, unknown>;
|
|
4
|
+
export declare const pushNotificationsCapabilities: () => PushNotificationsCapabilities;
|
|
5
|
+
export declare function withPushNotificationsCapability<T extends PushNotificationsCapabilities>(capabilities: T): T;
|
|
6
|
+
export declare function withPushNotificationsClientCapability<T extends Metadata>(metadata: T): T & PushNotificationsClientCapabilitiesMeta;
|
|
7
|
+
export declare function hasPushNotificationsCapability(capabilities: PushNotificationsCapabilities | null | undefined): boolean;
|
|
8
|
+
export declare function hasPerRequestPushNotificationsCapability(metadata: Metadata | null | undefined): boolean;
|
|
9
|
+
export declare function resolveServerPushNotificationsSupport(input: {
|
|
10
|
+
discovery?: PushNotificationsCapabilities | null;
|
|
11
|
+
initialization?: PushNotificationsCapabilities | null;
|
|
12
|
+
}): boolean;
|
|
13
|
+
export declare function missingPushNotificationsCapabilityError(): {
|
|
14
|
+
code: typeof MISSING_REQUIRED_CLIENT_CAPABILITY_CODE;
|
|
15
|
+
message: string;
|
|
16
|
+
data: MissingRequiredClientCapabilityData;
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,uCAAuC,EAExC,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EACV,mCAAmC,EACnC,6BAA6B,EAC7B,uCAAuC,EACxC,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE/C,eAAO,MAAM,6BAA6B,QACpC,6BAEF,CAAC;AAEL,wBAAgB,+BAA+B,CAC7C,CAAC,SAAS,6BAA6B,EACvC,YAAY,EAAE,CAAC,GAAG,CAAC,CAQpB;AAED,wBAAgB,qCAAqC,CAAC,CAAC,SAAS,QAAQ,EACtE,QAAQ,EAAE,CAAC,GACV,CAAC,GAAG,uCAAuC,CAW7C;AAED,wBAAgB,8BAA8B,CAC5C,YAAY,EAAE,6BAA6B,GAAG,IAAI,GAAG,SAAS,GAC7D,OAAO,CAKT;AAED,wBAAgB,wCAAwC,CACtD,QAAQ,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,GACpC,OAAO,CAOT;AAED,wBAAgB,qCAAqC,CAAC,KAAK,EAAE;IAC3D,SAAS,CAAC,EAAE,6BAA6B,GAAG,IAAI,CAAC;IACjD,cAAc,CAAC,EAAE,6BAA6B,GAAG,IAAI,CAAC;CACvD,GAAG,OAAO,CAKV;AAED,wBAAgB,uCAAuC,IAAI;IACzD,IAAI,EAAE,OAAO,uCAAuC,CAAC;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,mCAAmC,CAAC;CAC3C,CAMA"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CLIENT_CAPABILITIES_META_KEY, MISSING_REQUIRED_CLIENT_CAPABILITY_CODE, PUSH_NOTIFICATIONS_EXTENSION_ID, } from './constants.js';
|
|
2
|
+
export const pushNotificationsCapabilities = () => ({
|
|
3
|
+
extensions: { [PUSH_NOTIFICATIONS_EXTENSION_ID]: {} },
|
|
4
|
+
});
|
|
5
|
+
export function withPushNotificationsCapability(capabilities) {
|
|
6
|
+
return {
|
|
7
|
+
...capabilities,
|
|
8
|
+
extensions: {
|
|
9
|
+
...capabilities.extensions,
|
|
10
|
+
[PUSH_NOTIFICATIONS_EXTENSION_ID]: {},
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export function withPushNotificationsClientCapability(metadata) {
|
|
15
|
+
const current = metadata[CLIENT_CAPABILITIES_META_KEY];
|
|
16
|
+
const capabilities = typeof current === 'object' && current !== null
|
|
17
|
+
? current
|
|
18
|
+
: {};
|
|
19
|
+
return {
|
|
20
|
+
...metadata,
|
|
21
|
+
[CLIENT_CAPABILITIES_META_KEY]: withPushNotificationsCapability(capabilities),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function hasPushNotificationsCapability(capabilities) {
|
|
25
|
+
return (capabilities?.extensions !== undefined &&
|
|
26
|
+
Object.hasOwn(capabilities.extensions, PUSH_NOTIFICATIONS_EXTENSION_ID));
|
|
27
|
+
}
|
|
28
|
+
export function hasPerRequestPushNotificationsCapability(metadata) {
|
|
29
|
+
const value = metadata?.[CLIENT_CAPABILITIES_META_KEY];
|
|
30
|
+
return (typeof value === 'object' &&
|
|
31
|
+
value !== null &&
|
|
32
|
+
hasPushNotificationsCapability(value));
|
|
33
|
+
}
|
|
34
|
+
export function resolveServerPushNotificationsSupport(input) {
|
|
35
|
+
if (input.discovery !== undefined && input.discovery !== null) {
|
|
36
|
+
return hasPushNotificationsCapability(input.discovery);
|
|
37
|
+
}
|
|
38
|
+
return hasPushNotificationsCapability(input.initialization);
|
|
39
|
+
}
|
|
40
|
+
export function missingPushNotificationsCapabilityError() {
|
|
41
|
+
return {
|
|
42
|
+
code: MISSING_REQUIRED_CLIENT_CAPABILITY_CODE,
|
|
43
|
+
message: 'Missing required client capability',
|
|
44
|
+
data: { requiredCapabilities: pushNotificationsCapabilities() },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=capabilities.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAC5B,uCAAuC,EACvC,+BAA+B,GAChC,MAAM,gBAAgB,CAAC;AASxB,MAAM,CAAC,MAAM,6BAA6B,GACxC,GAAkC,EAAE,CAAC,CAAC;IACpC,UAAU,EAAE,EAAE,CAAC,+BAA+B,CAAC,EAAE,EAAE,EAAE;CACtD,CAAC,CAAC;AAEL,MAAM,UAAU,+BAA+B,CAE7C,YAAe;IACf,OAAO;QACL,GAAG,YAAY;QACf,UAAU,EAAE;YACV,GAAG,YAAY,CAAC,UAAU;YAC1B,CAAC,+BAA+B,CAAC,EAAE,EAAE;SACtC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qCAAqC,CACnD,QAAW;IAEX,MAAM,OAAO,GAAG,QAAQ,CAAC,4BAA4B,CAAC,CAAC;IACvD,MAAM,YAAY,GAChB,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI;QAC7C,CAAC,CAAE,OAAyC;QAC5C,CAAC,CAAC,EAAE,CAAC;IACT,OAAO;QACL,GAAG,QAAQ;QACX,CAAC,4BAA4B,CAAC,EAC5B,+BAA+B,CAAC,YAAY,CAAC;KAChD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B,CAC5C,YAA8D;IAE9D,OAAO,CACL,YAAY,EAAE,UAAU,KAAK,SAAS;QACtC,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,+BAA+B,CAAC,CACxE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wCAAwC,CACtD,QAAqC;IAErC,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC,4BAA4B,CAAC,CAAC;IACvD,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACd,8BAA8B,CAAC,KAAsC,CAAC,CACvE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,qCAAqC,CAAC,KAGrD;IACC,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QAC9D,OAAO,8BAA8B,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,8BAA8B,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,uCAAuC;IAKrD,OAAO;QACL,IAAI,EAAE,uCAAuC;QAC7C,OAAO,EAAE,oCAAoC;QAC7C,IAAI,EAAE,EAAE,oBAAoB,EAAE,6BAA6B,EAAE,EAAE;KAChE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,yCAAyC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,cAAc,yCAAyC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Client, RequestOptions } from '@modelcontextprotocol/client';
|
|
2
|
+
import type { AcknowledgeEventsParams, GetEventsParams, GetEventsResult, PushNotificationNotification, PushNotificationsSubscription, ServerDiscoverResult } from '../spec.types.js';
|
|
3
|
+
export type PushNotificationsClientProtocol = Pick<Client, 'getProtocolEra' | 'getServerCapabilities' | 'registerCapabilities' | 'request' | 'setNotificationHandler'> & {
|
|
4
|
+
assertCanSetRequestHandler?(method: string): void;
|
|
5
|
+
};
|
|
6
|
+
export interface RegisterPushNotificationsClientOptions {
|
|
7
|
+
onEvent?(notification: PushNotificationNotification): void | Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export interface PushNotificationsRequestOptions {
|
|
10
|
+
metadata?: Record<string, unknown>;
|
|
11
|
+
request?: RequestOptions;
|
|
12
|
+
}
|
|
13
|
+
export interface PushNotificationsSubscriptionHandle {
|
|
14
|
+
readonly closed: Promise<void>;
|
|
15
|
+
}
|
|
16
|
+
export interface RegisteredPushNotificationsClient {
|
|
17
|
+
discover(options?: PushNotificationsRequestOptions): Promise<ServerDiscoverResult>;
|
|
18
|
+
getEvents(params?: GetEventsParams, options?: PushNotificationsRequestOptions): Promise<GetEventsResult>;
|
|
19
|
+
acknowledgeEvents(params: AcknowledgeEventsParams, options?: PushNotificationsRequestOptions): Promise<void>;
|
|
20
|
+
listen(subscription?: PushNotificationsSubscription, options?: PushNotificationsRequestOptions): Promise<PushNotificationsSubscriptionHandle>;
|
|
21
|
+
serverSupportsPushNotifications(options?: PushNotificationsRequestOptions): Promise<boolean>;
|
|
22
|
+
acknowledgedSubscription(): PushNotificationsSubscription | undefined;
|
|
23
|
+
}
|
|
24
|
+
export declare function registerPushNotificationsClient(client: PushNotificationsClientProtocol, options?: RegisterPushNotificationsClientOptions): RegisteredPushNotificationsClient;
|
|
25
|
+
//# sourceMappingURL=register-push-notifications-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-push-notifications-client.d.ts","sourceRoot":"","sources":["../../src/client/register-push-notifications-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AAuB3E,OAAO,KAAK,EACV,uBAAuB,EACvB,eAAe,EACf,eAAe,EACf,4BAA4B,EAE5B,6BAA6B,EAC7B,oBAAoB,EACrB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,+BAA+B,GAAG,IAAI,CAChD,MAAM,EACJ,gBAAgB,GAChB,uBAAuB,GACvB,sBAAsB,GACtB,SAAS,GACT,wBAAwB,CAC3B,GAAG;IACF,0BAA0B,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACnD,CAAC;AAEF,MAAM,WAAW,sCAAsC;IACrD,OAAO,CAAC,CAAC,YAAY,EAAE,4BAA4B,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5E;AAED,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,WAAW,mCAAmC;IAClD,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,iCAAiC;IAChD,QAAQ,CACN,OAAO,CAAC,EAAE,+BAA+B,GACxC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IACjC,SAAS,CACP,MAAM,CAAC,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,+BAA+B,GACxC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5B,iBAAiB,CACf,MAAM,EAAE,uBAAuB,EAC/B,OAAO,CAAC,EAAE,+BAA+B,GACxC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,MAAM,CACJ,YAAY,CAAC,EAAE,6BAA6B,EAC5C,OAAO,CAAC,EAAE,+BAA+B,GACxC,OAAO,CAAC,mCAAmC,CAAC,CAAC;IAChD,+BAA+B,CAC7B,OAAO,CAAC,EAAE,+BAA+B,GACxC,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,wBAAwB,IAAI,6BAA6B,GAAG,SAAS,CAAC;CACvE;AAYD,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,+BAA+B,EACvC,OAAO,GAAE,sCAA2C,GACnD,iCAAiC,CAoJnC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { resolveServerPushNotificationsSupport, withPushNotificationsCapability, withPushNotificationsClientCapability, } from '../capabilities.js';
|
|
2
|
+
import { PUSH_NOTIFICATIONS_ACK_METHOD, PUSH_NOTIFICATIONS_GET_METHOD, PUSH_NOTIFICATIONS_NOTIFICATION_METHOD, SERVER_DISCOVER_METHOD, SUBSCRIPTIONS_ACKNOWLEDGED_METHOD, SUBSCRIPTIONS_LISTEN_METHOD, } from '../constants.js';
|
|
3
|
+
import { AcknowledgeEventsResultSchema, GetEventsResultSchema, PushNotificationNotificationParamsSchema, ServerDiscoverResultSchema, SubscriptionsAcknowledgedNotificationSchema, SubscriptionsListenResultSchema, } from '../generated/schema.js';
|
|
4
|
+
function paramsWithCapability(params, metadata) {
|
|
5
|
+
return {
|
|
6
|
+
...params,
|
|
7
|
+
_meta: withPushNotificationsClientCapability(metadata ?? {}),
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
export function registerPushNotificationsClient(client, options = {}) {
|
|
11
|
+
client.registerCapabilities(withPushNotificationsCapability({}));
|
|
12
|
+
let discoveryCapabilities;
|
|
13
|
+
let discoveryRequest;
|
|
14
|
+
let acknowledged;
|
|
15
|
+
let resolveAcknowledgement;
|
|
16
|
+
const markServerSupport = () => {
|
|
17
|
+
discoveryCapabilities = withPushNotificationsCapability({});
|
|
18
|
+
};
|
|
19
|
+
const discover = (requestOptions) => {
|
|
20
|
+
if (discoveryRequest)
|
|
21
|
+
return discoveryRequest;
|
|
22
|
+
discoveryRequest = client
|
|
23
|
+
.request({
|
|
24
|
+
method: SERVER_DISCOVER_METHOD,
|
|
25
|
+
params: paramsWithCapability({}, requestOptions?.metadata),
|
|
26
|
+
}, ServerDiscoverResultSchema, requestOptions?.request)
|
|
27
|
+
.then((result) => {
|
|
28
|
+
discoveryCapabilities = result.capabilities;
|
|
29
|
+
return result;
|
|
30
|
+
});
|
|
31
|
+
discoveryRequest.catch(() => {
|
|
32
|
+
discoveryRequest = undefined;
|
|
33
|
+
});
|
|
34
|
+
return discoveryRequest;
|
|
35
|
+
};
|
|
36
|
+
const serverSupportsPushNotifications = async (requestOptions) => {
|
|
37
|
+
const initialization = client.getServerCapabilities();
|
|
38
|
+
if (discoveryCapabilities !== undefined ||
|
|
39
|
+
client.getProtocolEra() !== undefined ||
|
|
40
|
+
initialization !== undefined) {
|
|
41
|
+
return resolveServerPushNotificationsSupport({
|
|
42
|
+
discovery: discoveryCapabilities,
|
|
43
|
+
initialization,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
await discover(requestOptions);
|
|
47
|
+
return resolveServerPushNotificationsSupport({
|
|
48
|
+
discovery: discoveryCapabilities,
|
|
49
|
+
initialization: client.getServerCapabilities(),
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
const requireServerSupport = async (requestOptions) => {
|
|
53
|
+
if (!(await serverSupportsPushNotifications(requestOptions))) {
|
|
54
|
+
throw new Error('Server does not support the Push Notifications extension');
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
client.setNotificationHandler(PUSH_NOTIFICATIONS_NOTIFICATION_METHOD, { params: PushNotificationNotificationParamsSchema }, async (params) => {
|
|
58
|
+
markServerSupport();
|
|
59
|
+
await options.onEvent?.({
|
|
60
|
+
jsonrpc: '2.0',
|
|
61
|
+
method: PUSH_NOTIFICATIONS_NOTIFICATION_METHOD,
|
|
62
|
+
params,
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
client.setNotificationHandler(SUBSCRIPTIONS_ACKNOWLEDGED_METHOD, { params: SubscriptionsAcknowledgedNotificationSchema.shape.params }, (params) => {
|
|
66
|
+
markServerSupport();
|
|
67
|
+
acknowledged = params.notifications['io.stagewise/push-notifications'];
|
|
68
|
+
resolveAcknowledgement?.();
|
|
69
|
+
resolveAcknowledgement = undefined;
|
|
70
|
+
});
|
|
71
|
+
return {
|
|
72
|
+
discover,
|
|
73
|
+
async getEvents(params = {}, requestOptions) {
|
|
74
|
+
await requireServerSupport(requestOptions);
|
|
75
|
+
return client.request({
|
|
76
|
+
method: PUSH_NOTIFICATIONS_GET_METHOD,
|
|
77
|
+
params: paramsWithCapability(params, requestOptions?.metadata),
|
|
78
|
+
}, GetEventsResultSchema, requestOptions?.request);
|
|
79
|
+
},
|
|
80
|
+
async acknowledgeEvents(params, requestOptions) {
|
|
81
|
+
await requireServerSupport(requestOptions);
|
|
82
|
+
await client.request({
|
|
83
|
+
method: PUSH_NOTIFICATIONS_ACK_METHOD,
|
|
84
|
+
params: paramsWithCapability(params, requestOptions?.metadata),
|
|
85
|
+
}, AcknowledgeEventsResultSchema, requestOptions?.request);
|
|
86
|
+
},
|
|
87
|
+
async listen(subscription = {}, requestOptions) {
|
|
88
|
+
await requireServerSupport(requestOptions);
|
|
89
|
+
const acknowledgement = new Promise((resolve) => {
|
|
90
|
+
resolveAcknowledgement = resolve;
|
|
91
|
+
});
|
|
92
|
+
const request = client.request({
|
|
93
|
+
method: SUBSCRIPTIONS_LISTEN_METHOD,
|
|
94
|
+
params: paramsWithCapability({
|
|
95
|
+
notifications: {
|
|
96
|
+
'io.stagewise/push-notifications': subscription,
|
|
97
|
+
},
|
|
98
|
+
}, requestOptions?.metadata),
|
|
99
|
+
}, SubscriptionsListenResultSchema, {
|
|
100
|
+
...requestOptions?.request,
|
|
101
|
+
onprogress: requestOptions?.request?.onprogress ?? (() => undefined),
|
|
102
|
+
resetTimeoutOnProgress: true,
|
|
103
|
+
});
|
|
104
|
+
const closed = request.then(() => undefined);
|
|
105
|
+
void closed.catch(() => undefined);
|
|
106
|
+
await Promise.race([acknowledgement, closed]).catch((error) => {
|
|
107
|
+
resolveAcknowledgement = undefined;
|
|
108
|
+
throw error;
|
|
109
|
+
});
|
|
110
|
+
return { closed };
|
|
111
|
+
},
|
|
112
|
+
serverSupportsPushNotifications,
|
|
113
|
+
acknowledgedSubscription: () => acknowledged,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=register-push-notifications-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-push-notifications-client.js","sourceRoot":"","sources":["../../src/client/register-push-notifications-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const PUSH_NOTIFICATIONS_EXTENSION_ID = "io.stagewise/push-notifications";
|
|
2
|
+
export declare const PUSH_NOTIFICATIONS_GET_METHOD = "io.stagewise/push-notifications/get";
|
|
3
|
+
export declare const PUSH_NOTIFICATIONS_ACK_METHOD = "io.stagewise/push-notifications/ack";
|
|
4
|
+
export declare const PUSH_NOTIFICATIONS_NOTIFICATION_METHOD = "io.stagewise/push-notifications/event";
|
|
5
|
+
export declare const SERVER_DISCOVER_METHOD = "server/discover";
|
|
6
|
+
export declare const SUBSCRIPTIONS_LISTEN_METHOD = "subscriptions/listen";
|
|
7
|
+
export declare const SUBSCRIPTIONS_ACKNOWLEDGED_METHOD = "notifications/subscriptions/acknowledged";
|
|
8
|
+
export declare const CLIENT_CAPABILITIES_META_KEY = "io.modelcontextprotocol/clientCapabilities";
|
|
9
|
+
export declare const MISSING_REQUIRED_CLIENT_CAPABILITY_CODE = -32003;
|
|
10
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,+BAA+B,oCACT,CAAC;AACpC,eAAO,MAAM,6BAA6B,wCAA2C,CAAC;AACtF,eAAO,MAAM,6BAA6B,wCAA2C,CAAC;AACtF,eAAO,MAAM,sCAAsC,0CACV,CAAC;AAC1C,eAAO,MAAM,sBAAsB,oBAAoB,CAAC;AACxD,eAAO,MAAM,2BAA2B,yBAAyB,CAAC;AAClE,eAAO,MAAM,iCAAiC,6CACF,CAAC;AAC7C,eAAO,MAAM,4BAA4B,+CACK,CAAC;AAC/C,eAAO,MAAM,uCAAuC,SAAS,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const PUSH_NOTIFICATIONS_EXTENSION_ID = 'io.stagewise/push-notifications';
|
|
2
|
+
export const PUSH_NOTIFICATIONS_GET_METHOD = `${PUSH_NOTIFICATIONS_EXTENSION_ID}/get`;
|
|
3
|
+
export const PUSH_NOTIFICATIONS_ACK_METHOD = `${PUSH_NOTIFICATIONS_EXTENSION_ID}/ack`;
|
|
4
|
+
export const PUSH_NOTIFICATIONS_NOTIFICATION_METHOD = 'io.stagewise/push-notifications/event';
|
|
5
|
+
export const SERVER_DISCOVER_METHOD = 'server/discover';
|
|
6
|
+
export const SUBSCRIPTIONS_LISTEN_METHOD = 'subscriptions/listen';
|
|
7
|
+
export const SUBSCRIPTIONS_ACKNOWLEDGED_METHOD = 'notifications/subscriptions/acknowledged';
|
|
8
|
+
export const CLIENT_CAPABILITIES_META_KEY = 'io.modelcontextprotocol/clientCapabilities';
|
|
9
|
+
export const MISSING_REQUIRED_CLIENT_CAPABILITY_CODE = -32003;
|
|
10
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,+BAA+B,GAC1C,iCAAiC,CAAC;AACpC,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,+BAA+B,MAAM,CAAC;AACtF,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAG,+BAA+B,MAAM,CAAC;AACtF,MAAM,CAAC,MAAM,sCAAsC,GACjD,uCAAuC,CAAC;AAC1C,MAAM,CAAC,MAAM,sBAAsB,GAAG,iBAAiB,CAAC;AACxD,MAAM,CAAC,MAAM,2BAA2B,GAAG,sBAAsB,CAAC;AAClE,MAAM,CAAC,MAAM,iCAAiC,GAC5C,0CAA0C,CAAC;AAC7C,MAAM,CAAC,MAAM,4BAA4B,GACvC,4CAA4C,CAAC;AAC/C,MAAM,CAAC,MAAM,uCAAuC,GAAG,CAAC,KAAK,CAAC"}
|