Skip to content

Commit 27d6a07

Browse files
authored
Merge branch 'main' into patch-2
2 parents b3c9d58 + 88537df commit 27d6a07

File tree

4 files changed

+182
-41
lines changed

4 files changed

+182
-41
lines changed

everything/CLAUDE.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# MCP "Everything" Server - Development Guidelines
2+
3+
## Build, Test & Run Commands
4+
- Build: `npm run build` - Compiles TypeScript to JavaScript
5+
- Watch mode: `npm run watch` - Watches for changes and rebuilds automatically
6+
- Run server: `npm run start` - Starts the MCP server using stdio transport
7+
- Run SSE server: `npm run start:sse` - Starts the MCP server with SSE transport
8+
- Prepare release: `npm run prepare` - Builds the project for publishing
9+
10+
## Code Style Guidelines
11+
- Use ES modules with `.js` extension in import paths
12+
- Strictly type all functions and variables with TypeScript
13+
- Follow zod schema patterns for tool input validation
14+
- Prefer async/await over callbacks and Promise chains
15+
- Place all imports at top of file, grouped by external then internal
16+
- Use descriptive variable names that clearly indicate purpose
17+
- Implement proper cleanup for timers and resources in server shutdown
18+
- Follow camelCase for variables/functions, PascalCase for types/classes, UPPER_CASE for constants
19+
- Handle errors with try/catch blocks and provide clear error messages
20+
- Use consistent indentation (2 spaces) and trailing commas in multi-line objects

everything/README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ This MCP server attempts to exercise all the features of the MCP protocol. It is
6363
}
6464
```
6565

66+
8. `getResourceReference`
67+
- Returns a resource reference that can be used by MCP clients
68+
- Inputs:
69+
- `resourceId` (number, 1-100): ID of the resource to reference
70+
- Returns: A resource reference with:
71+
- Text introduction
72+
- Embedded resource with `type: "resource"`
73+
- Text instruction for using the resource URI
74+
6675
### Resources
6776

6877
The server provides 100 test resources in two formats:
@@ -96,6 +105,13 @@ Resource features:
96105
- `style` (string): Output style preference
97106
- Returns: Multi-turn conversation with images
98107

108+
3. `resource_prompt`
109+
- Demonstrates embedding resource references in prompts
110+
- Required arguments:
111+
- `resourceId` (number): ID of the resource to embed (1-100)
112+
- Returns: Multi-turn conversation with an embedded resource reference
113+
- Shows how to include resources directly in prompt messages
114+
99115
### Logging
100116

101117
The server sends random-leveled log messages every 15 seconds, e.g.:

everything/everything.ts

Lines changed: 132 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,21 @@ const EXAMPLE_COMPLETIONS = {
6262
const GetTinyImageSchema = z.object({});
6363

6464
const AnnotatedMessageSchema = z.object({
65-
messageType: z.enum(["error", "success", "debug"])
65+
messageType: z
66+
.enum(["error", "success", "debug"])
6667
.describe("Type of message to demonstrate different annotation patterns"),
67-
includeImage: z.boolean().default(false)
68-
.describe("Whether to include an example image")
68+
includeImage: z
69+
.boolean()
70+
.default(false)
71+
.describe("Whether to include an example image"),
72+
});
73+
74+
const GetResourceReferenceSchema = z.object({
75+
resourceId: z
76+
.number()
77+
.min(1)
78+
.max(100)
79+
.describe("ID of the resource to reference (1-100)"),
6980
});
7081

7182
enum ToolName {
@@ -76,11 +87,13 @@ enum ToolName {
7687
SAMPLE_LLM = "sampleLLM",
7788
GET_TINY_IMAGE = "getTinyImage",
7889
ANNOTATED_MESSAGE = "annotatedMessage",
90+
GET_RESOURCE_REFERENCE = "getResourceReference",
7991
}
8092

8193
enum PromptName {
8294
SIMPLE = "simple_prompt",
8395
COMPLEX = "complex_prompt",
96+
RESOURCE = "resource_prompt",
8497
}
8598

8699
export const createServer = () => {
@@ -97,7 +110,7 @@ export const createServer = () => {
97110
logging: {},
98111
completions: {},
99112
},
100-
},
113+
}
101114
);
102115

103116
let subscriptions: Set<string> = new Set();
@@ -116,36 +129,37 @@ export const createServer = () => {
116129
let logLevel: LoggingLevel = "debug";
117130
let logsUpdateInterval: NodeJS.Timeout | undefined;
118131
const messages = [
119-
{level: "debug", data: "Debug-level message"},
120-
{level: "info", data: "Info-level message"},
121-
{level: "notice", data: "Notice-level message"},
122-
{level: "warning", data: "Warning-level message"},
123-
{level: "error", data: "Error-level message"},
124-
{level: "critical", data: "Critical-level message"},
125-
{level: "alert", data: "Alert level-message"},
126-
{level: "emergency", data: "Emergency-level message"}
127-
]
128-
129-
const isMessageIgnored = (level:LoggingLevel):boolean => {
132+
{ level: "debug", data: "Debug-level message" },
133+
{ level: "info", data: "Info-level message" },
134+
{ level: "notice", data: "Notice-level message" },
135+
{ level: "warning", data: "Warning-level message" },
136+
{ level: "error", data: "Error-level message" },
137+
{ level: "critical", data: "Critical-level message" },
138+
{ level: "alert", data: "Alert level-message" },
139+
{ level: "emergency", data: "Emergency-level message" },
140+
];
141+
142+
const isMessageIgnored = (level: LoggingLevel): boolean => {
130143
const currentLevel = messages.findIndex((msg) => logLevel === msg.level);
131-
const messageLevel = messages.findIndex((msg) => level === msg.level);
144+
const messageLevel = messages.findIndex((msg) => level === msg.level);
132145
return messageLevel < currentLevel;
133-
}
146+
};
134147

135148
// Set up update interval for random log messages
136149
logsUpdateInterval = setInterval(() => {
137150
let message = {
138151
method: "notifications/message",
139152
params: messages[Math.floor(Math.random() * messages.length)],
140-
}
141-
if (!isMessageIgnored(message.params.level as LoggingLevel)) server.notification(message);
153+
};
154+
if (!isMessageIgnored(message.params.level as LoggingLevel))
155+
server.notification(message);
142156
}, 15000);
143157

144158
// Helper method to request sampling from client
145159
const requestSampling = async (
146160
context: string,
147161
uri: string,
148-
maxTokens: number = 100,
162+
maxTokens: number = 100
149163
) => {
150164
const request: CreateMessageRequest = {
151165
method: "sampling/createMessage",
@@ -281,6 +295,17 @@ export const createServer = () => {
281295
},
282296
],
283297
},
298+
{
299+
name: PromptName.RESOURCE,
300+
description: "A prompt that includes an embedded resource reference",
301+
arguments: [
302+
{
303+
name: "resourceId",
304+
description: "Resource ID to include (1-100)",
305+
required: true,
306+
},
307+
],
308+
},
284309
],
285310
};
286311
});
@@ -331,6 +356,37 @@ export const createServer = () => {
331356
};
332357
}
333358

359+
if (name === PromptName.RESOURCE) {
360+
const resourceId = parseInt(args?.resourceId as string, 10);
361+
if (isNaN(resourceId) || resourceId < 1 || resourceId > 100) {
362+
throw new Error(
363+
`Invalid resourceId: ${args?.resourceId}. Must be a number between 1 and 100.`
364+
);
365+
}
366+
367+
const resourceIndex = resourceId - 1;
368+
const resource = ALL_RESOURCES[resourceIndex];
369+
370+
return {
371+
messages: [
372+
{
373+
role: "user",
374+
content: {
375+
type: "text",
376+
text: `This prompt includes Resource ${resourceId}. Please analyze the following resource:`,
377+
},
378+
},
379+
{
380+
role: "user",
381+
content: {
382+
type: "resource",
383+
resource: resource,
384+
},
385+
},
386+
],
387+
};
388+
}
389+
334390
throw new Error(`Unknown prompt: ${name}`);
335391
});
336392

@@ -348,7 +404,8 @@ export const createServer = () => {
348404
},
349405
{
350406
name: ToolName.PRINT_ENV,
351-
description: "Prints all environment variables, helpful for debugging MCP server configuration",
407+
description:
408+
"Prints all environment variables, helpful for debugging MCP server configuration",
352409
inputSchema: zodToJsonSchema(PrintEnvSchema) as ToolInput,
353410
},
354411
{
@@ -369,9 +426,16 @@ export const createServer = () => {
369426
},
370427
{
371428
name: ToolName.ANNOTATED_MESSAGE,
372-
description: "Demonstrates how annotations can be used to provide metadata about content",
429+
description:
430+
"Demonstrates how annotations can be used to provide metadata about content",
373431
inputSchema: zodToJsonSchema(AnnotatedMessageSchema) as ToolInput,
374432
},
433+
{
434+
name: ToolName.GET_RESOURCE_REFERENCE,
435+
description:
436+
"Returns a resource reference that can be used by MCP clients",
437+
inputSchema: zodToJsonSchema(GetResourceReferenceSchema) as ToolInput,
438+
},
375439
];
376440

377441
return { tools };
@@ -408,7 +472,7 @@ export const createServer = () => {
408472

409473
for (let i = 1; i < steps + 1; i++) {
410474
await new Promise((resolve) =>
411-
setTimeout(resolve, stepDuration * 1000),
475+
setTimeout(resolve, stepDuration * 1000)
412476
);
413477

414478
if (progressToken !== undefined) {
@@ -451,10 +515,12 @@ export const createServer = () => {
451515
const result = await requestSampling(
452516
prompt,
453517
ToolName.SAMPLE_LLM,
454-
maxTokens,
518+
maxTokens
455519
);
456520
return {
457-
content: [{ type: "text", text: `LLM sampling result: ${result.content.text}` }],
521+
content: [
522+
{ type: "text", text: `LLM sampling result: ${result.content.text}` },
523+
],
458524
};
459525
}
460526

@@ -479,6 +545,35 @@ export const createServer = () => {
479545
};
480546
}
481547

548+
if (name === ToolName.GET_RESOURCE_REFERENCE) {
549+
const validatedArgs = GetResourceReferenceSchema.parse(args);
550+
const resourceId = validatedArgs.resourceId;
551+
552+
const resourceIndex = resourceId - 1;
553+
if (resourceIndex < 0 || resourceIndex >= ALL_RESOURCES.length) {
554+
throw new Error(`Resource with ID ${resourceId} does not exist`);
555+
}
556+
557+
const resource = ALL_RESOURCES[resourceIndex];
558+
559+
return {
560+
content: [
561+
{
562+
type: "text",
563+
text: `Returning resource reference for Resource ${resourceId}:`,
564+
},
565+
{
566+
type: "resource",
567+
resource: resource,
568+
},
569+
{
570+
type: "text",
571+
text: `You can access this resource using the URI: ${resource.uri}`,
572+
},
573+
],
574+
};
575+
}
576+
482577
if (name === ToolName.ANNOTATED_MESSAGE) {
483578
const { messageType, includeImage } = AnnotatedMessageSchema.parse(args);
484579

@@ -491,26 +586,26 @@ export const createServer = () => {
491586
text: "Error: Operation failed",
492587
annotations: {
493588
priority: 1.0, // Errors are highest priority
494-
audience: ["user", "assistant"] // Both need to know about errors
495-
}
589+
audience: ["user", "assistant"], // Both need to know about errors
590+
},
496591
});
497592
} else if (messageType === "success") {
498593
content.push({
499594
type: "text",
500595
text: "Operation completed successfully",
501596
annotations: {
502597
priority: 0.7, // Success messages are important but not critical
503-
audience: ["user"] // Success mainly for user consumption
504-
}
598+
audience: ["user"], // Success mainly for user consumption
599+
},
505600
});
506601
} else if (messageType === "debug") {
507602
content.push({
508603
type: "text",
509604
text: "Debug: Cache hit ratio 0.95, latency 150ms",
510605
annotations: {
511606
priority: 0.3, // Debug info is low priority
512-
audience: ["assistant"] // Technical details for assistant
513-
}
607+
audience: ["assistant"], // Technical details for assistant
608+
},
514609
});
515610
}
516611

@@ -522,8 +617,8 @@ export const createServer = () => {
522617
mimeType: "image/png",
523618
annotations: {
524619
priority: 0.5,
525-
audience: ["user"] // Images primarily for user visualization
526-
}
620+
audience: ["user"], // Images primarily for user visualization
621+
},
527622
});
528623
}
529624

@@ -541,18 +636,19 @@ export const createServer = () => {
541636
if (!resourceId) return { completion: { values: [] } };
542637

543638
// Filter resource IDs that start with the input value
544-
const values = EXAMPLE_COMPLETIONS.resourceId.filter(id =>
639+
const values = EXAMPLE_COMPLETIONS.resourceId.filter((id) =>
545640
id.startsWith(argument.value)
546641
);
547642
return { completion: { values, hasMore: false, total: values.length } };
548643
}
549644

550645
if (ref.type === "ref/prompt") {
551646
// Handle completion for prompt arguments
552-
const completions = EXAMPLE_COMPLETIONS[argument.name as keyof typeof EXAMPLE_COMPLETIONS];
647+
const completions =
648+
EXAMPLE_COMPLETIONS[argument.name as keyof typeof EXAMPLE_COMPLETIONS];
553649
if (!completions) return { completion: { values: [] } };
554650

555-
const values = completions.filter(value =>
651+
const values = completions.filter((value) =>
556652
value.startsWith(argument.value)
557653
);
558654
return { completion: { values, hasMore: false, total: values.length } };

github/operations/pulls.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,20 @@ export const CreatePullRequestReviewSchema = z.object({
112112
commit_id: z.string().optional().describe("The SHA of the commit that needs a review"),
113113
body: z.string().describe("The body text of the review"),
114114
event: z.enum(['APPROVE', 'REQUEST_CHANGES', 'COMMENT']).describe("The review action to perform"),
115-
comments: z.array(z.object({
116-
path: z.string().describe("The relative path to the file being commented on"),
117-
position: z.number().describe("The position in the diff where you want to add a review comment"),
118-
body: z.string().describe("Text of the review comment")
119-
})).optional().describe("Comments to post as part of the review")
115+
comments: z.array(
116+
z.union([
117+
z.object({
118+
path: z.string().describe("The relative path to the file being commented on"),
119+
position: z.number().describe("The position in the diff where you want to add a review comment"),
120+
body: z.string().describe("Text of the review comment")
121+
}),
122+
z.object({
123+
path: z.string().describe("The relative path to the file being commented on"),
124+
line: z.number().describe("The line number in the file where you want to add a review comment"),
125+
body: z.string().describe("Text of the review comment")
126+
})
127+
])
128+
).optional().describe("Comments to post as part of the review (specify either position or line, not both)")
120129
});
121130

122131
export const MergePullRequestSchema = z.object({

0 commit comments

Comments
 (0)