Merge branch 'main' into claude/issue-2361-20250817-1626

This commit is contained in:
Cliff Hall
2025-08-28 17:23:24 -04:00
committed by GitHub
4 changed files with 186 additions and 23 deletions

9
package-lock.json generated
View File

@@ -5818,7 +5818,7 @@
"version": "0.6.2", "version": "0.6.2",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.12.0", "@modelcontextprotocol/sdk": "^1.17.4",
"express": "^4.21.1", "express": "^4.21.1",
"zod": "^3.23.8", "zod": "^3.23.8",
"zod-to-json-schema": "^3.23.5" "zod-to-json-schema": "^3.23.5"
@@ -5833,9 +5833,9 @@
} }
}, },
"src/everything/node_modules/@modelcontextprotocol/sdk": { "src/everything/node_modules/@modelcontextprotocol/sdk": {
"version": "1.12.3", "version": "1.17.4",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.3.tgz", "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.4.tgz",
"integrity": "sha512-DyVYSOafBvk3/j1Oka4z5BWT8o4AFmoNyZY9pALOm7Lh3GZglR71Co4r4dEUoqDWdDazIZQHBe7J2Nwkg6gHgQ==", "integrity": "sha512-zq24hfuAmmlNZvik0FLI58uE5sriN0WWsQzIlYnzSuKDAHFqJtBFrl/LfB1NLgJT5Y7dEBzaX4yAKqOPrcetaw==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ajv": "^6.12.6", "ajv": "^6.12.6",
@@ -5843,6 +5843,7 @@
"cors": "^2.8.5", "cors": "^2.8.5",
"cross-spawn": "^7.0.5", "cross-spawn": "^7.0.5",
"eventsource": "^3.0.2", "eventsource": "^3.0.2",
"eventsource-parser": "^3.0.0",
"express": "^5.0.1", "express": "^5.0.1",
"express-rate-limit": "^7.5.0", "express-rate-limit": "^7.5.0",
"pkce-challenge": "^5.0.0", "pkce-challenge": "^5.0.0",

View File

@@ -89,6 +89,13 @@ This MCP server attempts to exercise all the features of the MCP protocol. It is
- `structuredContent` field conformant to the output schema - `structuredContent` field conformant to the output schema
- A backward compatible Text Content field, a SHOULD advisory in the specification - A backward compatible Text Content field, a SHOULD advisory in the specification
11. `listRoots`
- Lists the current MCP roots provided by the client
- Demonstrates the roots protocol capability even though this server doesn't access files
- No inputs required
- Returns: List of current roots with their URIs and names, or a message if no roots are set
- Shows how servers can interact with the MCP roots protocol
### Resources ### Resources
The server provides 100 test resources in two formats: The server provides 100 test resources in two formats:
@@ -129,6 +136,18 @@ Resource features:
- Returns: Multi-turn conversation with an embedded resource reference - Returns: Multi-turn conversation with an embedded resource reference
- Shows how to include resources directly in prompt messages - Shows how to include resources directly in prompt messages
### Roots
The server demonstrates the MCP roots protocol capability:
- Declares `roots: { listChanged: true }` capability to indicate support for roots
- Handles `roots/list_changed` notifications from clients
- Requests initial roots during server initialization
- Provides a `listRoots` tool to display current roots
- Logs roots-related events for demonstration purposes
Note: This server doesn't actually access files, but demonstrates how servers can interact with the roots protocol for clients that need to understand which directories are available for file operations.
### Logging ### Logging
The server sends random-leveled log messages every 15 seconds, e.g.: The server sends random-leveled log messages every 15 seconds, e.g.:

View File

@@ -1,6 +1,7 @@
import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { import {
CallToolRequestSchema, CallToolRequestSchema,
ClientCapabilities,
CompleteRequestSchema, CompleteRequestSchema,
CreateMessageRequest, CreateMessageRequest,
CreateMessageResultSchema, CreateMessageResultSchema,
@@ -12,11 +13,13 @@ import {
LoggingLevel, LoggingLevel,
ReadResourceRequestSchema, ReadResourceRequestSchema,
Resource, Resource,
RootsListChangedNotificationSchema,
SetLevelRequestSchema, SetLevelRequestSchema,
SubscribeRequestSchema, SubscribeRequestSchema,
Tool, Tool,
ToolSchema, ToolSchema,
UnsubscribeRequestSchema, UnsubscribeRequestSchema,
type Root,
} from "@modelcontextprotocol/sdk/types.js"; } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod"; import { z } from "zod";
import { zodToJsonSchema } from "zod-to-json-schema"; import { zodToJsonSchema } from "zod-to-json-schema";
@@ -96,6 +99,8 @@ const GetResourceLinksSchema = z.object({
.describe("Number of resource links to return (1-10)"), .describe("Number of resource links to return (1-10)"),
}); });
const ListRootsSchema = z.object({});
const StructuredContentSchema = { const StructuredContentSchema = {
input: z.object({ input: z.object({
location: z location: z
@@ -129,7 +134,8 @@ enum ToolName {
GET_RESOURCE_REFERENCE = "getResourceReference", GET_RESOURCE_REFERENCE = "getResourceReference",
ELICITATION = "startElicitation", ELICITATION = "startElicitation",
GET_RESOURCE_LINKS = "getResourceLinks", GET_RESOURCE_LINKS = "getResourceLinks",
STRUCTURED_CONTENT = "structuredContent" STRUCTURED_CONTENT = "structuredContent",
LIST_ROOTS = "listRoots"
} }
enum PromptName { enum PromptName {
@@ -158,8 +164,7 @@ export const createServer = () => {
resources: { subscribe: true }, resources: { subscribe: true },
tools: {}, tools: {},
logging: {}, logging: {},
completions: {}, completions: {}
elicitation: {},
}, },
instructions instructions
} }
@@ -171,6 +176,12 @@ export const createServer = () => {
let logLevel: LoggingLevel = "debug"; let logLevel: LoggingLevel = "debug";
let logsUpdateInterval: NodeJS.Timeout | undefined; let logsUpdateInterval: NodeJS.Timeout | undefined;
// Store client capabilities
let clientCapabilities: ClientCapabilities | undefined;
// Roots state management
let currentRoots: Root[] = [];
let clientSupportsRoots = false;
const messages = [ const messages = [
{ level: "debug", data: "Debug-level message" }, { level: "debug", data: "Debug-level message" },
{ level: "info", data: "Info-level message" }, { level: "info", data: "Info-level message" },
@@ -511,11 +522,6 @@ export const createServer = () => {
"Returns a resource reference that can be used by MCP clients", "Returns a resource reference that can be used by MCP clients",
inputSchema: zodToJsonSchema(GetResourceReferenceSchema) as ToolInput, inputSchema: zodToJsonSchema(GetResourceReferenceSchema) as ToolInput,
}, },
{
name: ToolName.ELICITATION,
description: "Demonstrates the Elicitation feature by asking the user to provide information about their favorite color, number, and pets.",
inputSchema: zodToJsonSchema(ElicitationSchema) as ToolInput,
},
{ {
name: ToolName.GET_RESOURCE_LINKS, name: ToolName.GET_RESOURCE_LINKS,
description: description:
@@ -530,6 +536,17 @@ export const createServer = () => {
outputSchema: zodToJsonSchema(StructuredContentSchema.output) as ToolOutput, outputSchema: zodToJsonSchema(StructuredContentSchema.output) as ToolOutput,
}, },
]; ];
if (clientCapabilities!.roots) tools.push ({
name: ToolName.LIST_ROOTS,
description:
"Lists the current MCP roots provided by the client. Demonstrates the roots protocol capability even though this server doesn't access files.",
inputSchema: zodToJsonSchema(ListRootsSchema) as ToolInput,
});
if (clientCapabilities!.elicitation) tools.push ({
name: ToolName.ELICITATION,
description: "Demonstrates the Elicitation feature by asking the user to provide information about their favorite color, number, and pets.",
inputSchema: zodToJsonSchema(ElicitationSchema) as ToolInput,
});
return { tools }; return { tools };
}); });
@@ -791,8 +808,7 @@ export const createServer = () => {
type: "resource_link", type: "resource_link",
uri: resource.uri, uri: resource.uri,
name: resource.name, name: resource.name,
description: `Resource ${i + 1}: ${ description: `Resource ${i + 1}: ${resource.mimeType === "text/plain"
resource.mimeType === "text/plain"
? "plaintext resource" ? "plaintext resource"
: "binary blob resource" : "binary blob resource"
}`, }`,
@@ -824,6 +840,52 @@ export const createServer = () => {
}; };
} }
if (name === ToolName.LIST_ROOTS) {
ListRootsSchema.parse(args);
if (!clientSupportsRoots) {
return {
content: [
{
type: "text",
text: "The MCP client does not support the roots protocol.\n\n" +
"This means the server cannot access information about the client's workspace directories or file system roots."
}
]
};
}
if (currentRoots.length === 0) {
return {
content: [
{
type: "text",
text: "The client supports roots but no roots are currently configured.\n\n" +
"This could mean:\n" +
"1. The client hasn't provided any roots yet\n" +
"2. The client provided an empty roots list\n" +
"3. The roots configuration is still being loaded"
}
]
};
}
const rootsList = currentRoots.map((root, index) => {
return `${index + 1}. ${root.name || 'Unnamed Root'}\n URI: ${root.uri}`;
}).join('\n\n');
return {
content: [
{
type: "text",
text: `Current MCP Roots (${currentRoots.length} total):\n\n${rootsList}\n\n` +
"Note: This server demonstrates the roots protocol capability but doesn't actually access files. " +
"The roots are provided by the MCP client and can be used by servers that need file system access."
}
]
};
}
throw new Error(`Unknown tool: ${name}`); throw new Error(`Unknown tool: ${name}`);
}); });
@@ -873,6 +935,87 @@ export const createServer = () => {
return {}; return {};
}); });
// Roots protocol handlers
server.setNotificationHandler(RootsListChangedNotificationSchema, async () => {
try {
// Request the updated roots list from the client
const response = await server.listRoots();
if (response && 'roots' in response) {
currentRoots = response.roots;
// Log the roots update for demonstration
await server.notification({
method: "notifications/message",
params: {
level: "info",
logger: "everything-server",
data: `Roots updated: ${currentRoots.length} root(s) received from client`,
},
});
}
} catch (error) {
await server.notification({
method: "notifications/message",
params: {
level: "error",
logger: "everything-server",
data: `Failed to request roots from client: ${error instanceof Error ? error.message : String(error)}`,
},
});
}
});
// Handle post-initialization setup for roots
server.oninitialized = async () => {
clientCapabilities = server.getClientCapabilities();
if (clientCapabilities?.roots) {
clientSupportsRoots = true;
try {
const response = await server.listRoots();
if (response && 'roots' in response) {
currentRoots = response.roots;
await server.notification({
method: "notifications/message",
params: {
level: "info",
logger: "everything-server",
data: `Initial roots received: ${currentRoots.length} root(s) from client`,
},
});
} else {
await server.notification({
method: "notifications/message",
params: {
level: "warning",
logger: "everything-server",
data: "Client returned no roots set",
},
});
}
} catch (error) {
await server.notification({
method: "notifications/message",
params: {
level: "error",
logger: "everything-server",
data: `Failed to request initial roots from client: ${error instanceof Error ? error.message : String(error)}`,
},
});
}
} else {
await server.notification({
method: "notifications/message",
params: {
level: "info",
logger: "everything-server",
data: "Client does not support MCP roots protocol",
},
});
}
};
const cleanup = async () => { const cleanup = async () => {
if (subsUpdateInterval) clearInterval(subsUpdateInterval); if (subsUpdateInterval) clearInterval(subsUpdateInterval);
if (logsUpdateInterval) clearInterval(logsUpdateInterval); if (logsUpdateInterval) clearInterval(logsUpdateInterval);

View File

@@ -22,7 +22,7 @@
"start:streamableHttp": "node dist/streamableHttp.js" "start:streamableHttp": "node dist/streamableHttp.js"
}, },
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.12.0", "@modelcontextprotocol/sdk": "^1.17.4",
"express": "^4.21.1", "express": "^4.21.1",
"zod": "^3.23.8", "zod": "^3.23.8",
"zod-to-json-schema": "^3.23.5" "zod-to-json-schema": "^3.23.5"