Skip to content
Draft
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
78 changes: 77 additions & 1 deletion package-lock.json

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

18 changes: 18 additions & 0 deletions src/everything/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,24 @@

This MCP server attempts to exercise all the features of the MCP protocol. It is not intended to be a useful server, but rather a test server for builders of MCP clients. It implements prompts, tools, resources, sampling, and more to showcase MCP capabilities.

## Protocol support

The server is **dual-era** — one codebase serving both protocol generations, so a client
can be exercised against either without changing servers:

| Era | Revisions | Negotiated by |
| ---------- | ---------------------------- | --------------------------------------------------- |
| **legacy** | `2024-10-07` … `2025-11-25` | the `initialize` handshake |
| **modern** | `2026-07-28` and later | a [`server/discover`](https://modelcontextprotocol.io/specification/draft/server/discover) probe |

Both `stdio` and `streamableHttp` serve either era; `streamableHttp` does it from a single
endpoint. The deprecated `sse` transport is legacy-only. See
[Architecture](docs/architecture.md) for what actually differs between them, and
[Server Features](docs/features.md) for how the tools behave on each.

Requires **Node.js >= 20** and is ESM-only, inherited from the v2 SDK
(`@modelcontextprotocol/server`).

## Tools, Resources, Prompts, and Other Features

A complete list of the registered MCP primitives and other protocol features demonstrated can be found in the [Server Features](docs/features.md) document.
Expand Down
2 changes: 1 addition & 1 deletion src/everything/__tests__/prompts.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect, vi } from 'vitest';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { McpServer } from '@modelcontextprotocol/server';
import { registerSimplePrompt } from '../prompts/simple.js';
import { registerArgumentsPrompt } from '../prompts/args.js';
import { registerPromptWithCompletions } from '../prompts/completions.js';
Expand Down
157 changes: 59 additions & 98 deletions src/everything/__tests__/registrations.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect, vi } from 'vitest';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { describe, it, expect, vi } from "vitest";
import { McpServer } from "@modelcontextprotocol/server";

// Create mock server
function createMockServer() {
Expand All @@ -16,139 +16,100 @@ function createMockServer() {
} 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');
describe("Registration Index Files", () => {
describe("tools/index.ts", () => {
it("should register all tools unconditionally", 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);
// Every tool registers up front. There is no capability-gated second
// pass any more: tools needing elicitation / sampling / roots return
// `inputRequired(...)`, and the SDK refuses the embedded request with
// `-32021` at dispatch when the caller lacks the capability -- on both
// eras. Note the mock advertises NO client capabilities, and the
// capability-gated tools are still expected below.
expect(mockServer.registerTool).toHaveBeenCalledTimes(17);

// 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');
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");

// Formerly "conditional" tools -- now registered up front.
expect(registeredTools).toContain("get-roots-list");
expect(registeredTools).toContain("trigger-elicitation-request");
expect(registeredTools).toContain("trigger-url-elicitation");
expect(registeredTools).toContain("trigger-sampling-request");

// Formerly registered through the experimental tasks API, now an
// ordinary multi-round-trip tool.
expect(registeredTools).toContain("simulate-research-query");
});

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: { url: {} },
sampling: {},
})),
},
experimental: {
tasks: {
registerToolTask: vi.fn(),
},
},
} as unknown as McpServer;

registerConditionalTools(mockServerWithCapabilities);

// Should register 4 conditional tools via registerTool when all capabilities
// are present. Task-based tools register via registerToolTask (counted separately),
// so they are not included in this registerTool count.
expect(mockServerWithCapabilities.registerTool).toHaveBeenCalledTimes(4);

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-url-elicitation');
expect(registeredTools).toContain('trigger-sampling-request');

// Task-based tools are registered via experimental.tasks.registerToolTask
expect(mockServerWithCapabilities.experimental.tasks.registerToolTask).toHaveBeenCalled();
});
it("should not expose a separate conditional registration pass", async () => {
const toolsIndex = await import("../tools/index.js");

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();
expect("registerConditionalTools" in toolsIndex).toBe(false);
});
});

describe('prompts/index.ts', () => {
it('should register all prompts', async () => {
const { registerPrompts } = await import('../prompts/index.js');
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');
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');
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');
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');
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(typeof instructions).toBe("string");
expect(instructions.length).toBeGreaterThan(0);
});
});
Expand Down
2 changes: 1 addition & 1 deletion src/everything/__tests__/resources.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/sdk/server/mcp.js';
import { McpServer, ResourceTemplate } from '@modelcontextprotocol/server';
import {
textResource,
blobResource,
Expand Down
Loading
Loading