feat: first round of mcp standalone server

This commit is contained in:
2026-02-28 21:23:22 +01:00
parent 1fc2003260
commit c358e1b11c
67 changed files with 3426 additions and 901 deletions

View File

@@ -60,22 +60,34 @@ function createMockMediaEngine() {
function createMockScriptEngine() {
return {
createScript: vi.fn().mockResolvedValue({
createDraftScript: vi.fn().mockResolvedValue({
id: 'script-1', title: 'Test', slug: 'test', kind: 'macro',
entrypoint: 'main.py', content: '', enabled: true, version: 1,
filePath: '/test', createdAt: new Date(), updatedAt: new Date(),
}),
publishScript: vi.fn().mockResolvedValue({
id: 'script-1', title: 'Test', slug: 'test', kind: 'macro',
entrypoint: 'main.py', content: '', enabled: true, version: 1,
filePath: '/test', createdAt: new Date(), updatedAt: new Date(),
}),
deleteDraftScript: vi.fn().mockResolvedValue(true),
validateScript: vi.fn().mockResolvedValue({ valid: true, errors: [] }),
};
}
function createMockTemplateEngine() {
return {
createTemplate: vi.fn().mockResolvedValue({
createDraftTemplate: vi.fn().mockResolvedValue({
id: 'tpl-1', title: 'Test', slug: 'test', kind: 'post',
enabled: true, version: 1, filePath: '/test', content: '',
createdAt: new Date(), updatedAt: new Date(),
}),
publishTemplate: vi.fn().mockResolvedValue({
id: 'tpl-1', title: 'Test', slug: 'test', kind: 'post',
enabled: true, version: 1, filePath: '/test', content: '',
createdAt: new Date(), updatedAt: new Date(),
}),
deleteDraftTemplate: vi.fn().mockResolvedValue(true),
validateTemplate: vi.fn().mockResolvedValue({ valid: true, errors: [] }),
};
}
@@ -110,13 +122,13 @@ function createDependencies() {
const mockTagEngine = createMockTagEngine();
const deps: MCPServerDependencies = {
getPostEngine: () => mockPostEngine,
getMediaEngine: () => mockMediaEngine,
getScriptEngine: () => mockScriptEngine,
getTemplateEngine: () => mockTemplateEngine,
getMetaEngine: () => mockMetaEngine,
getPostMediaEngine: () => mockPostMediaEngine,
getTagEngine: () => mockTagEngine,
postEngine: mockPostEngine,
mediaEngine: mockMediaEngine,
scriptEngine: mockScriptEngine,
templateEngine: mockTemplateEngine,
metaEngine: mockMetaEngine,
postMediaEngine: mockPostMediaEngine,
tagEngine: mockTagEngine,
};
return { deps, mockPostEngine, mockMediaEngine, mockScriptEngine, mockTemplateEngine, mockMetaEngine, mockPostMediaEngine, mockTagEngine };
@@ -358,25 +370,21 @@ describe('MCPServer', () => {
it('accepts a proposeScript proposal by creating script', async () => {
const proposalId = server.proposalStore.create('proposeScript', {
title: 'My Script', kind: 'macro', content: 'print("hello")',
scriptId: 'script-1',
});
const result = await server.acceptProposal(proposalId);
expect(result.success).toBe(true);
expect(mockScriptEngine.createScript).toHaveBeenCalledWith({
title: 'My Script', kind: 'macro', content: 'print("hello")',
});
expect(mockScriptEngine.publishScript).toHaveBeenCalledWith('script-1');
expect(server.proposalStore.get(proposalId)).toBeUndefined();
});
it('accepts a proposeTemplate proposal by creating template', async () => {
const proposalId = server.proposalStore.create('proposeTemplate', {
title: 'My Template', kind: 'post', content: '<h1>{{ title }}</h1>',
templateId: 'tpl-1',
});
const result = await server.acceptProposal(proposalId);
expect(result.success).toBe(true);
expect(mockTemplateEngine.createTemplate).toHaveBeenCalledWith({
title: 'My Template', kind: 'post', content: '<h1>{{ title }}</h1>',
});
expect(mockTemplateEngine.publishTemplate).toHaveBeenCalledWith('tpl-1');
});
it('accepts a proposeMediaMetadata proposal by updating media', async () => {
@@ -415,9 +423,10 @@ describe('MCPServer', () => {
});
it('discards a proposeScript proposal by removing from store', async () => {
const proposalId = server.proposalStore.create('proposeScript', { title: 'Script' });
const proposalId = server.proposalStore.create('proposeScript', { scriptId: 'script-1' });
const result = await server.discardProposal(proposalId);
expect(result.success).toBe(true);
expect(mockScriptEngine.deleteDraftScript).toHaveBeenCalledWith('script-1');
expect(server.proposalStore.get(proposalId)).toBeUndefined();
});
@@ -830,7 +839,10 @@ describe('MCPServer', () => {
const proposal = server.proposalStore.get(parsed.proposalId);
expect(proposal).toBeDefined();
expect(proposal!.type).toBe('proposeScript');
expect(proposal!.data.content).toBe('print("hi")');
expect(mockScriptEngine.createDraftScript).toHaveBeenCalledWith({
title: 'My Script', kind: 'macro', content: 'print("hi")', entrypoint: undefined,
});
expect(proposal!.data.scriptId).toBe('script-1');
});
it('propose_script calls validateScript and includes validation result in preview', async () => {