@objectstack/plugin-mcp-server 4.0.3 → 4.0.5

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,239 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- import { describe, it, expect, vi, beforeEach } from 'vitest';
4
- import { MCPServerPlugin } from '../plugin.js';
5
-
6
- // ---------------------------------------------------------------------------
7
- // Mock PluginContext
8
- // ---------------------------------------------------------------------------
9
-
10
- function createMockPluginContext(services: Record<string, any> = {}) {
11
- const serviceRegistry = new Map<string, any>(Object.entries(services));
12
-
13
- return {
14
- registerService: vi.fn((name: string, service: any) => {
15
- serviceRegistry.set(name, service);
16
- }),
17
- getService: vi.fn(<T>(name: string): T => {
18
- if (!serviceRegistry.has(name)) {
19
- throw new Error(`Service "${name}" not found`);
20
- }
21
- return serviceRegistry.get(name) as T;
22
- }),
23
- replaceService: vi.fn(),
24
- getServices: vi.fn(() => serviceRegistry),
25
- hook: vi.fn(),
26
- trigger: vi.fn(async () => {}),
27
- logger: {
28
- info: vi.fn(),
29
- warn: vi.fn(),
30
- error: vi.fn(),
31
- debug: vi.fn(),
32
- },
33
- getKernel: vi.fn(() => ({})),
34
- };
35
- }
36
-
37
- function createMockAIService() {
38
- return {
39
- chat: vi.fn(),
40
- complete: vi.fn(),
41
- toolRegistry: {
42
- getAll: () => [
43
- { name: 'list_objects', description: 'List objects', parameters: {} },
44
- { name: 'query_records', description: 'Query records', parameters: {} },
45
- ],
46
- execute: vi.fn(async () => ({
47
- type: 'tool-result',
48
- toolCallId: 'test',
49
- toolName: 'test',
50
- output: { type: 'text', value: '{}' },
51
- })),
52
- },
53
- };
54
- }
55
-
56
- function createMockMetadataService() {
57
- return {
58
- listObjects: vi.fn(async () => []),
59
- getObject: vi.fn(async () => null),
60
- get: vi.fn(async () => null),
61
- list: vi.fn(async () => []),
62
- exists: vi.fn(async () => false),
63
- getRegisteredTypes: vi.fn(async () => ['object', 'agent']),
64
- register: vi.fn(),
65
- unregister: vi.fn(),
66
- };
67
- }
68
-
69
- function createMockDataEngine() {
70
- return {
71
- find: vi.fn(async () => []),
72
- findOne: vi.fn(async () => null),
73
- insert: vi.fn(),
74
- update: vi.fn(),
75
- delete: vi.fn(),
76
- count: vi.fn(async () => 0),
77
- aggregate: vi.fn(async () => []),
78
- };
79
- }
80
-
81
- // ---------------------------------------------------------------------------
82
- // Tests
83
- // ---------------------------------------------------------------------------
84
-
85
- describe('MCPServerPlugin', () => {
86
- const originalEnv = { ...process.env };
87
-
88
- beforeEach(() => {
89
- process.env = { ...originalEnv };
90
- // Ensure MCP_SERVER_ENABLED is NOT set unless explicitly done in a test
91
- delete process.env.MCP_SERVER_ENABLED;
92
- delete process.env.MCP_SERVER_NAME;
93
- delete process.env.MCP_SERVER_TRANSPORT;
94
- });
95
-
96
- describe('metadata', () => {
97
- it('should have correct plugin metadata', () => {
98
- const plugin = new MCPServerPlugin();
99
- expect(plugin.name).toBe('com.objectstack.plugin-mcp-server');
100
- expect(plugin.version).toBe('1.0.0');
101
- expect(plugin.type).toBe('standard');
102
- });
103
- });
104
-
105
- describe('init', () => {
106
- it('should register MCP service on init', async () => {
107
- const plugin = new MCPServerPlugin();
108
- const ctx = createMockPluginContext();
109
-
110
- await plugin.init(ctx as any);
111
-
112
- expect(ctx.registerService).toHaveBeenCalledWith('mcp', expect.any(Object));
113
- expect(ctx.logger.info).toHaveBeenCalledWith('[MCP] Plugin initialized');
114
- });
115
-
116
- it('should respect MCP_SERVER_NAME env var', async () => {
117
- process.env.MCP_SERVER_NAME = 'custom-name';
118
- const plugin = new MCPServerPlugin();
119
- const ctx = createMockPluginContext();
120
-
121
- await plugin.init(ctx as any);
122
-
123
- const registeredRuntime = (ctx.registerService as any).mock.calls[0][1];
124
- expect(registeredRuntime).toBeDefined();
125
- });
126
-
127
- it('should use plugin option name when env var not set', async () => {
128
- const plugin = new MCPServerPlugin({ name: 'my-mcp-server' });
129
- const ctx = createMockPluginContext();
130
-
131
- await plugin.init(ctx as any);
132
-
133
- expect(ctx.registerService).toHaveBeenCalledWith('mcp', expect.any(Object));
134
- });
135
- });
136
-
137
- describe('start', () => {
138
- it('should bridge tools when AI service is available', async () => {
139
- const aiService = createMockAIService();
140
- const metadataService = createMockMetadataService();
141
- const dataEngine = createMockDataEngine();
142
-
143
- const ctx = createMockPluginContext({
144
- ai: aiService,
145
- metadata: metadataService,
146
- data: dataEngine,
147
- });
148
-
149
- const plugin = new MCPServerPlugin();
150
- await plugin.init(ctx as any);
151
- await plugin.start(ctx as any);
152
-
153
- expect(ctx.logger.info).toHaveBeenCalledWith(
154
- expect.stringContaining('[MCP] Server ready but not started'),
155
- );
156
- expect(ctx.trigger).toHaveBeenCalledWith('mcp:ready', expect.any(Object));
157
- });
158
-
159
- it('should handle missing AI service gracefully', async () => {
160
- const metadataService = createMockMetadataService();
161
- const ctx = createMockPluginContext({ metadata: metadataService });
162
-
163
- const plugin = new MCPServerPlugin();
164
- await plugin.init(ctx as any);
165
- await plugin.start(ctx as any);
166
-
167
- expect(ctx.logger.debug).toHaveBeenCalledWith(
168
- '[MCP] AI service not available, skipping tool bridging',
169
- );
170
- });
171
-
172
- it('should handle missing metadata service gracefully', async () => {
173
- const aiService = createMockAIService();
174
- const ctx = createMockPluginContext({ ai: aiService });
175
-
176
- const plugin = new MCPServerPlugin();
177
- await plugin.init(ctx as any);
178
- await plugin.start(ctx as any);
179
-
180
- expect(ctx.logger.debug).toHaveBeenCalledWith(
181
- '[MCP] Metadata service not available, skipping resource bridging',
182
- );
183
- });
184
-
185
- it('should handle missing data engine gracefully', async () => {
186
- const aiService = createMockAIService();
187
- const metadataService = createMockMetadataService();
188
- const ctx = createMockPluginContext({ ai: aiService, metadata: metadataService });
189
-
190
- const plugin = new MCPServerPlugin();
191
- await plugin.init(ctx as any);
192
- await plugin.start(ctx as any);
193
-
194
- expect(ctx.logger.debug).toHaveBeenCalledWith(
195
- '[MCP] Data engine not available, skipping record resources',
196
- );
197
- });
198
-
199
- it('should not auto-start when MCP_SERVER_ENABLED is not set', async () => {
200
- const ctx = createMockPluginContext();
201
-
202
- const plugin = new MCPServerPlugin();
203
- await plugin.init(ctx as any);
204
- await plugin.start(ctx as any);
205
-
206
- expect(ctx.logger.info).toHaveBeenCalledWith(
207
- expect.stringContaining('[MCP] Server ready but not started'),
208
- );
209
- });
210
-
211
- it('should trigger mcp:ready hook', async () => {
212
- const ctx = createMockPluginContext();
213
-
214
- const plugin = new MCPServerPlugin();
215
- await plugin.init(ctx as any);
216
- await plugin.start(ctx as any);
217
-
218
- expect(ctx.trigger).toHaveBeenCalledWith('mcp:ready', expect.any(Object));
219
- });
220
- });
221
-
222
- describe('destroy', () => {
223
- it('should clean up on destroy', async () => {
224
- const ctx = createMockPluginContext();
225
-
226
- const plugin = new MCPServerPlugin();
227
- await plugin.init(ctx as any);
228
-
229
- // Should not throw
230
- await plugin.destroy();
231
- });
232
-
233
- it('should handle destroy without init', async () => {
234
- const plugin = new MCPServerPlugin();
235
- // Should not throw
236
- await plugin.destroy();
237
- });
238
- });
239
- });
package/src/index.ts DELETED
@@ -1,15 +0,0 @@
1
- // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
-
3
- /**
4
- * @objectstack/plugin-mcp-server
5
- *
6
- * MCP Runtime Server Plugin for ObjectStack.
7
- * Exposes all registered AI tools, data resources, and agent prompts
8
- * via the Model Context Protocol (MCP) for use by external AI clients
9
- * (Claude Desktop, Cursor, VS Code Copilot, etc.).
10
- */
11
-
12
- export { MCPServerPlugin } from './plugin.js';
13
- export type { MCPServerPluginOptions } from './plugin.js';
14
- export { MCPServerRuntime } from './mcp-server-runtime.js';
15
- export type { MCPServerRuntimeConfig } from './mcp-server-runtime.js';