@elementor/editor-mcp 4.1.0-788 → 4.1.0-790

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.
@@ -1,9 +1,10 @@
1
1
  import { z, type z3 } from '@elementor/schema';
2
- import { type AngieMcpSdk } from '@elementor-external/angie-sdk';
3
2
  import { McpServer, type ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js';
4
3
  import { type RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';
4
+ import { UriTemplate } from '@modelcontextprotocol/sdk/shared/uriTemplate.js';
5
5
  import { type ServerNotification, type ServerRequest } from '@modelcontextprotocol/sdk/types.js';
6
6
 
7
+ import { type IMcpRegistrationAdapter, type McpResourceHandler, type McpResourceUriOrTemplate } from './adapters/types';
7
8
  import {
8
9
  ANGIE_MODEL_PREFERENCES,
9
10
  ANGIE_REQUIRED_RESOURCES,
@@ -11,7 +12,6 @@ import {
11
12
  createDefaultModelPreferences,
12
13
  } from './angie-annotations';
13
14
  import { mockMcpRegistry } from './test-utils/mock-mcp-registry';
14
- import { getSDK } from './utils/get-sdk';
15
15
 
16
16
  type ZodRawShape = z3.ZodRawShape;
17
17
 
@@ -20,40 +20,56 @@ const mcpDescriptions: { [ namespace: string ]: string } = {};
20
20
  // @ts-ignore - QUnit fails this
21
21
  const isMcpRegistrationActivated = false || typeof globalThis.jest !== 'undefined';
22
22
 
23
- export const registerMcp = ( mcp: McpServer, name: string ) => {
24
- const mcpName = isAlphabet( name );
25
- mcpRegistry[ mcpName ] = mcp;
26
- };
23
+ const registrationAdapters: IMcpRegistrationAdapter[] = [];
24
+ const bufferedTools: Parameters< IMcpRegistrationAdapter[ 'onToolRegistered' ] >[] = [];
25
+ const bufferedResources: Parameters< IMcpRegistrationAdapter[ 'onResourceRegistered' ] >[] = [];
27
26
 
28
- export async function activateMcpRegistration( sdk: AngieMcpSdk, entries = Object.entries( mcpRegistry ), retry = 3 ) {
29
- if ( retry === 0 ) {
30
- /* eslint-disable-next-line no-console */
31
- console.error( 'Failed to register MCP after 3 retries. failed entries: ', entries );
32
- return;
33
- }
34
- if ( entries.length === 0 ) {
35
- return;
27
+ let resolveReady!: () => void;
28
+ const readyPromise = new Promise< void >( ( resolve ) => {
29
+ resolveReady = resolve;
30
+ } );
31
+
32
+ export const registerMcpAdapter = ( adapter: IMcpRegistrationAdapter ): void => {
33
+ registrationAdapters.push( adapter );
34
+ for ( const tool of bufferedTools ) {
35
+ try {
36
+ adapter.onToolRegistered( tool[ 0 ], tool[ 1 ] );
37
+ } catch {
38
+ // exit quietly
39
+ }
36
40
  }
37
- const failed = [];
38
- for await ( const entry of entries ) {
39
- const [ key, mcpServer ] = entry;
41
+ for ( const resource of bufferedResources ) {
40
42
  try {
41
- await sdk.registerLocalServer( {
42
- title: toMCPTitle( key ),
43
- name: `editor-${ key }`,
44
- server: mcpServer,
45
- version: '1.0.0',
46
- description: mcpDescriptions[ key ] || key,
47
- } );
43
+ adapter.onResourceRegistered( ...resource );
48
44
  } catch {
49
- failed.push( entry );
45
+ // exit quietly
50
46
  }
51
47
  }
52
- if ( failed.length > 0 ) {
53
- return activateMcpRegistration( sdk, failed, retry - 1 );
48
+ };
49
+
50
+ export const signalMcpReady = (): void => resolveReady();
51
+
52
+ export const activateAdapters = (): void => callAdapters( ( adapter ) => adapter.activate() );
53
+
54
+ function callAdapters( fn: ( adapter: IMcpRegistrationAdapter ) => void ): void {
55
+ for ( const adapter of registrationAdapters ) {
56
+ try {
57
+ fn( adapter );
58
+ } catch {
59
+ // adapter failed — exit quietly, continue to next
60
+ }
54
61
  }
55
62
  }
56
63
 
64
+ export const registerMcp = ( mcp: McpServer, name: string ) => {
65
+ const mcpName = isAlphabet( name );
66
+ mcpRegistry[ mcpName ] = mcp;
67
+ };
68
+
69
+ export const getRegisteredMcpServers = (): Array< [ string, McpServer, string ] > => {
70
+ return Object.entries( mcpRegistry ).map( ( [ key, server ] ) => [ key, server, mcpDescriptions[ key ] || key ] );
71
+ };
72
+
57
73
  const isAlphabet = ( str: string ): string | never => {
58
74
  const passes = !! str && /^[a-z_]+$/.test( str );
59
75
  if ( ! passes ) {
@@ -89,48 +105,50 @@ export const getMCPByDomain = ( namespace: string, options?: { instructions?: st
89
105
  },
90
106
  {
91
107
  instructions: options?.instructions,
108
+ capabilities: { resources: { subscribe: true } },
92
109
  }
93
110
  );
111
+ if ( !! options?.instructions ) {
112
+ callAdapters( ( adapter ) =>
113
+ adapter.onResourceRegistered( `${ mcpName }`, { uriTemplate: new UriTemplate( mcpName ) }, () =>
114
+ Promise.resolve( { contents: [ { text: options.instructions ?? '' } ] } )
115
+ )
116
+ );
117
+ }
94
118
  }
95
119
  const mcpServer = mcpRegistry[ namespace ];
96
- const { addTool } = createToolRegistry( mcpServer );
120
+ const { addTool } = createToolRegistry( mcpServer, mcpName );
97
121
  return {
98
- waitForReady: () => getSDK().waitForReady(),
122
+ waitForReady: () => readyPromise,
99
123
  // @ts-expect-error: TS is unable to infer the type here
100
124
  resource: async ( ...args: Parameters< McpServer[ 'registerResource' ] > ) => {
101
- await getSDK().waitForReady();
125
+ const [ name, uriOrTemplate, ...rest ] = args as [ string, unknown, ...unknown[] ];
126
+ const handler = rest[ rest.length - 1 ] as McpResourceHandler;
127
+ const resourceArgs: Parameters< IMcpRegistrationAdapter[ 'onResourceRegistered' ] > = [
128
+ name,
129
+ uriOrTemplate as McpResourceUriOrTemplate,
130
+ handler,
131
+ ];
132
+ bufferedResources.push( resourceArgs );
133
+ callAdapters( ( adapter ) => adapter.onResourceRegistered( ...resourceArgs ) );
102
134
  return mcpServer.registerResource( ...args );
103
135
  },
104
136
  sendResourceUpdated: ( ...args: Parameters< McpServer[ 'server' ][ 'sendResourceUpdated' ] > ) => {
105
- return getSDK()
106
- .waitForReady()
107
- .then( () => mcpServer.server.sendResourceUpdated( ...args ) )
108
- .catch( ( error: Error ) => {
109
- if ( error?.message?.includes( 'Not connected' ) ) {
110
- return; // Expected when no MCP client is connected yet
111
- }
112
- throw error;
113
- } );
137
+ callAdapters( ( adapter ) => adapter.sendResourceUpdated( { uri: args[ 0 ].uri } ) );
138
+ return Promise.resolve( mcpServer.server.sendResourceUpdated( ...args ) ).catch( ( error: Error ) => {
139
+ if ( error?.message?.includes( 'Not connected' ) ) {
140
+ return; // Expected when no MCP client is connected yet
141
+ }
142
+ if ( error?.message?.includes( 'does not support notifying about resources' ) ) {
143
+ return; // Server capability not declared — safe to ignore
144
+ }
145
+ throw error;
146
+ } );
114
147
  },
115
- mcpServer,
116
148
  addTool,
117
149
  setMCPDescription: ( description: string ) => {
118
150
  mcpDescriptions[ namespace ] = description;
119
151
  },
120
- getActiveChatInfo: () => {
121
- const info = localStorage.getItem( 'angie_active_chat_id' );
122
- if ( ! info ) {
123
- return {
124
- expiresAt: 0,
125
- sessionId: '',
126
- };
127
- }
128
- const rawData = JSON.parse( info );
129
- return {
130
- expiresAt: rawData.expiresAt as number,
131
- sessionId: rawData.sessionId as string,
132
- };
133
- },
134
152
  };
135
153
  };
136
154
 
@@ -139,10 +157,8 @@ export interface MCPRegistryEntry {
139
157
  opts: ToolRegistrationOptions< T, O >
140
158
  ) => void;
141
159
  setMCPDescription: ( description: string ) => void;
142
- getActiveChatInfo: () => { sessionId: string; expiresAt: number };
143
160
  sendResourceUpdated: McpServer[ 'server' ][ 'sendResourceUpdated' ];
144
161
  resource: McpServer[ 'registerResource' ];
145
- mcpServer: McpServer;
146
162
  waitForReady: () => Promise< void >;
147
163
  }
148
164
 
@@ -178,7 +194,7 @@ type ToolRegistrationOptions<
178
194
  modelPreferences?: AngieModelPreferences;
179
195
  };
180
196
 
181
- function createToolRegistry( server: McpServer ) {
197
+ function createToolRegistry( server: McpServer, serverName: string ) {
182
198
  function addTool<
183
199
  T extends undefined | z.ZodRawShape = undefined,
184
200
  O extends undefined | z.ZodRawShape = undefined,
@@ -246,6 +262,25 @@ function createToolRegistry( server: McpServer ) {
246
262
  },
247
263
  toolCallback
248
264
  );
265
+ const toolDescriptor = {
266
+ name: opts.name,
267
+ description: opts.description,
268
+ inputSchema: inputSchema as object,
269
+ execute: ( params: Record< string, unknown > ) =>
270
+ Promise.resolve(
271
+ toolCallback(
272
+ params as Parameters< typeof toolCallback >[ 0 ],
273
+ /* WebMCP: no protocol session — handlers must not rely on `extra` here */
274
+ {} as RequestHandlerExtra< ServerRequest, ServerNotification >
275
+ )
276
+ ),
277
+ };
278
+ const extraData = {
279
+ resources: [ `Server resource name: ${ serverName }, Required to fetch!` ],
280
+ requiredResources: opts.requiredResources?.map( ( resource ) => resource.uri ) ?? [],
281
+ };
282
+ bufferedTools.push( [ toolDescriptor, extraData ] );
283
+ callAdapters( ( adapter ) => adapter.onToolRegistered( toolDescriptor, extraData ) );
249
284
  if ( isMcpRegistrationActivated ) {
250
285
  server.sendToolListChanged();
251
286
  }
@@ -21,9 +21,6 @@ export const mockMcpRegistry = (): MCPRegistryEntry => {
21
21
  sendResourceUpdated: () => {},
22
22
  addTool: () => {},
23
23
  setMCPDescription: () => {},
24
- getActiveChatInfo() {
25
- return { sessionId: 'mock-session-id', expiresAt: Date.now() + 3600000 };
26
- },
27
24
  mcpServer: mock as McpServer,
28
25
  };
29
26
  };
@@ -0,0 +1,19 @@
1
+ export type ActiveChatInfo = {
2
+ sessionId: string;
3
+ expiresAt: number;
4
+ };
5
+
6
+ export const getActiveChatInfo = (): ActiveChatInfo => {
7
+ const info = localStorage.getItem( 'angie_active_chat_id' );
8
+ if ( ! info ) {
9
+ return {
10
+ expiresAt: 0,
11
+ sessionId: '',
12
+ };
13
+ }
14
+ const rawData = JSON.parse( info );
15
+ return {
16
+ expiresAt: rawData.expiresAt as number,
17
+ sessionId: rawData.sessionId as string,
18
+ };
19
+ };