Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

179 changes: 179 additions & 0 deletions src/everything/__tests__/prompts.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { describe, it, expect, vi } from 'vitest';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { registerSimplePrompt } from '../prompts/simple.js';
import { registerArgumentsPrompt } from '../prompts/args.js';
import { registerPromptWithCompletions } from '../prompts/completions.js';
import { registerEmbeddedResourcePrompt } from '../prompts/resource.js';

// Helper to capture registered prompt handlers
function createMockServer() {
const handlers: Map<string, Function> = new Map();
const configs: Map<string, any> = new Map();

const mockServer = {
registerPrompt: vi.fn((name: string, config: any, handler: Function) => {
handlers.set(name, handler);
configs.set(name, config);
}),
} as unknown as McpServer;

return { mockServer, handlers, configs };
}

describe('Prompts', () => {
describe('simple-prompt', () => {
it('should return fixed message with no arguments', () => {
const { mockServer, handlers } = createMockServer();
registerSimplePrompt(mockServer);

const handler = handlers.get('simple-prompt')!;
const result = handler();

expect(result).toEqual({
messages: [
{
role: 'user',
content: {
type: 'text',
text: 'This is a simple prompt without arguments.',
},
},
],
});
});
});

describe('args-prompt', () => {
it('should include city in message', () => {
const { mockServer, handlers } = createMockServer();
registerArgumentsPrompt(mockServer);

const handler = handlers.get('args-prompt')!;
const result = handler({ city: 'San Francisco' });

expect(result.messages[0].content.text).toBe("What's weather in San Francisco?");
});

it('should include city and state in message', () => {
const { mockServer, handlers } = createMockServer();
registerArgumentsPrompt(mockServer);

const handler = handlers.get('args-prompt')!;
const result = handler({ city: 'San Francisco', state: 'California' });

expect(result.messages[0].content.text).toBe(
"What's weather in San Francisco, California?"
);
});

it('should handle city only (optional state omitted)', () => {
const { mockServer, handlers } = createMockServer();
registerArgumentsPrompt(mockServer);

const handler = handlers.get('args-prompt')!;
const result = handler({ city: 'New York' });

expect(result.messages[0].content.text).toBe("What's weather in New York?");
expect(result.messages[0].content.text).not.toContain(',');
expect(result.messages[0].role).toBe('user');
expect(result.messages[0].content.type).toBe('text');
});
});

describe('completable-prompt', () => {
it('should generate promotion message with department and name', () => {
const { mockServer, handlers } = createMockServer();
registerPromptWithCompletions(mockServer);

const handler = handlers.get('completable-prompt')!;
const result = handler({ department: 'Engineering', name: 'Alice' });

expect(result.messages[0].content.text).toBe(
'Please promote Alice to the head of the Engineering team.'
);
});

it('should work with different departments', () => {
const { mockServer, handlers } = createMockServer();
registerPromptWithCompletions(mockServer);

const handler = handlers.get('completable-prompt')!;

const salesResult = handler({ department: 'Sales', name: 'David' });
expect(salesResult.messages[0].content.text).toContain('Sales');
expect(salesResult.messages[0].content.text).toContain('David');
expect(salesResult.messages[0].role).toBe('user');

const marketingResult = handler({ department: 'Marketing', name: 'Grace' });
expect(marketingResult.messages[0].content.text).toContain('Marketing');
expect(marketingResult.messages[0].content.text).toContain('Grace');
});
});

describe('resource-prompt', () => {
it('should return text resource reference', () => {
const { mockServer, handlers } = createMockServer();
registerEmbeddedResourcePrompt(mockServer);

const handler = handlers.get('resource-prompt')!;
const result = handler({ resourceType: 'Text', resourceId: '1' });

expect(result.messages).toHaveLength(2);
expect(result.messages[0].content.text).toContain('Text');
expect(result.messages[0].content.text).toContain('1');
expect(result.messages[1].content.type).toBe('resource');
expect(result.messages[1].content.resource.uri).toContain('text/1');
});

it('should return blob resource reference', () => {
const { mockServer, handlers } = createMockServer();
registerEmbeddedResourcePrompt(mockServer);

const handler = handlers.get('resource-prompt')!;
const result = handler({ resourceType: 'Blob', resourceId: '5' });

expect(result.messages[0].content.text).toContain('Blob');
expect(result.messages[1].content.resource.uri).toContain('blob/5');
});

it('should reject invalid resource type', () => {
const { mockServer, handlers } = createMockServer();
registerEmbeddedResourcePrompt(mockServer);

const handler = handlers.get('resource-prompt')!;
expect(() => handler({ resourceType: 'Invalid', resourceId: '1' })).toThrow(
'Invalid resourceType'
);
});

it('should reject invalid resource ID', () => {
const { mockServer, handlers } = createMockServer();
registerEmbeddedResourcePrompt(mockServer);

const handler = handlers.get('resource-prompt')!;
expect(() => handler({ resourceType: 'Text', resourceId: '-1' })).toThrow(
'Invalid resourceId'
);
expect(() => handler({ resourceType: 'Text', resourceId: '0' })).toThrow(
'Invalid resourceId'
);
expect(() => handler({ resourceType: 'Text', resourceId: 'abc' })).toThrow(
'Invalid resourceId'
);
});

it('should include both intro text and resource messages', () => {
const { mockServer, handlers } = createMockServer();
registerEmbeddedResourcePrompt(mockServer);

const handler = handlers.get('resource-prompt')!;
const result = handler({ resourceType: 'Text', resourceId: '3' });

expect(result.messages).toHaveLength(2);
expect(result.messages[0].role).toBe('user');
expect(result.messages[0].content.type).toBe('text');
expect(result.messages[1].role).toBe('user');
expect(result.messages[1].content.type).toBe('resource');
});
});
});
152 changes: 152 additions & 0 deletions src/everything/__tests__/registrations.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { describe, it, expect, vi } from 'vitest';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';

// Create mock server
function createMockServer() {
return {
registerTool: vi.fn(),
registerPrompt: vi.fn(),
registerResource: vi.fn(),
server: {
getClientCapabilities: vi.fn(() => ({})),
setRequestHandler: vi.fn(),
},
sendLoggingMessage: vi.fn(),
sendResourceUpdated: vi.fn(),
} as unknown as McpServer;
}

describe('Registration Index Files', () => {
describe('tools/index.ts', () => {
it('should register all standard tools', async () => {
const { registerTools } = await import('../tools/index.js');
const mockServer = createMockServer();

registerTools(mockServer);

// Should register 12 standard tools (non-conditional)
expect(mockServer.registerTool).toHaveBeenCalledTimes(12);

// Verify specific tools are registered
const registeredTools = (mockServer.registerTool as any).mock.calls.map(
(call: any[]) => call[0]
);
expect(registeredTools).toContain('echo');
expect(registeredTools).toContain('get-sum');
expect(registeredTools).toContain('get-env');
expect(registeredTools).toContain('get-tiny-image');
expect(registeredTools).toContain('get-structured-content');
expect(registeredTools).toContain('get-annotated-message');
expect(registeredTools).toContain('trigger-long-running-operation');
expect(registeredTools).toContain('get-resource-links');
expect(registeredTools).toContain('get-resource-reference');
expect(registeredTools).toContain('gzip-file-as-resource');
expect(registeredTools).toContain('toggle-simulated-logging');
expect(registeredTools).toContain('toggle-subscriber-updates');
});

it('should register conditional tools based on capabilities', async () => {
const { registerConditionalTools } = await import('../tools/index.js');

// Server with all capabilities including experimental tasks API
const mockServerWithCapabilities = {
registerTool: vi.fn(),
server: {
getClientCapabilities: vi.fn(() => ({
roots: {},
elicitation: {},
sampling: {},
})),
},
experimental: {
tasks: {
registerToolTask: vi.fn(),
},
},
} as unknown as McpServer;

registerConditionalTools(mockServerWithCapabilities);

// Should register 3 conditional tools + 3 task-based tools when all capabilities present
expect(mockServerWithCapabilities.registerTool).toHaveBeenCalledTimes(3);

const registeredTools = (
mockServerWithCapabilities.registerTool as any
).mock.calls.map((call: any[]) => call[0]);
expect(registeredTools).toContain('get-roots-list');
expect(registeredTools).toContain('trigger-elicitation-request');
expect(registeredTools).toContain('trigger-sampling-request');

// Task-based tools are registered via experimental.tasks.registerToolTask
expect(mockServerWithCapabilities.experimental.tasks.registerToolTask).toHaveBeenCalled();
});

it('should not register conditional tools when capabilities missing', async () => {
const { registerConditionalTools } = await import('../tools/index.js');

const mockServerNoCapabilities = {
registerTool: vi.fn(),
server: {
getClientCapabilities: vi.fn(() => ({})),
},
experimental: {
tasks: {
registerToolTask: vi.fn(),
},
},
} as unknown as McpServer;

registerConditionalTools(mockServerNoCapabilities);

// Should not register any capability-gated tools when capabilities are missing
expect(mockServerNoCapabilities.registerTool).not.toHaveBeenCalled();
});
});

describe('prompts/index.ts', () => {
it('should register all prompts', async () => {
const { registerPrompts } = await import('../prompts/index.js');
const mockServer = createMockServer();

registerPrompts(mockServer);

// Should register 4 prompts
expect(mockServer.registerPrompt).toHaveBeenCalledTimes(4);

const registeredPrompts = (mockServer.registerPrompt as any).mock.calls.map(
(call: any[]) => call[0]
);
expect(registeredPrompts).toContain('simple-prompt');
expect(registeredPrompts).toContain('args-prompt');
expect(registeredPrompts).toContain('completable-prompt');
expect(registeredPrompts).toContain('resource-prompt');
});
});

describe('resources/index.ts', () => {
it('should register resource templates', async () => {
const { registerResources } = await import('../resources/index.js');
const mockServer = createMockServer();

registerResources(mockServer);

// Should register at least the 2 resource templates (text and blob) plus file resources
expect(mockServer.registerResource).toHaveBeenCalled();
const registeredResources = (mockServer.registerResource as any).mock.calls.map(
(call: any[]) => call[0]
);
expect(registeredResources).toContain('Dynamic Text Resource');
expect(registeredResources).toContain('Dynamic Blob Resource');
});

it('should read instructions from file', async () => {
const { readInstructions } = await import('../resources/index.js');

const instructions = readInstructions();

// Should return a string (either content or error message)
expect(typeof instructions).toBe('string');
expect(instructions.length).toBeGreaterThan(0);
});
});
});
Loading