Skip to content

Commit 3713b15

Browse files
authored
Merge branch 'main' into patch-1
2 parents e5329f8 + e181222 commit 3713b15

File tree

17 files changed

+1344
-164
lines changed

17 files changed

+1344
-164
lines changed

README.md

Lines changed: 174 additions & 7 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 786 additions & 91 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/everything/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ Resource features:
9696
- `style` (string): Output style preference
9797
- Returns: Multi-turn conversation with images
9898

99+
### Logging
100+
101+
The server sends random-leveled log messages every 15 seconds, e.g.:
102+
103+
```json
104+
{
105+
"method": "notifications/message",
106+
"params": {
107+
"level": "info",
108+
"data": "Info-level message"
109+
}
110+
}
111+
```
112+
99113
## Usage with Claude Desktop
100114

101115
Add to your `claude_desktop_config.json`:

src/everything/everything.ts

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
ListResourcesRequestSchema,
1010
ListResourceTemplatesRequestSchema,
1111
ListToolsRequestSchema,
12+
LoggingLevel,
1213
ReadResourceRequestSchema,
1314
Resource,
1415
SetLevelRequestSchema,
@@ -99,10 +100,10 @@ export const createServer = () => {
99100
);
100101

101102
let subscriptions: Set<string> = new Set();
102-
let updateInterval: NodeJS.Timeout | undefined;
103-
103+
let subsUpdateInterval: NodeJS.Timeout | undefined;
104104
// Set up update interval for subscribed resources
105-
updateInterval = setInterval(() => {
105+
106+
subsUpdateInterval = setInterval(() => {
106107
for (const uri of subscriptions) {
107108
server.notification({
108109
method: "notifications/resources/updated",
@@ -111,6 +112,34 @@ export const createServer = () => {
111112
}
112113
}, 5000);
113114

115+
let logLevel: LoggingLevel = "debug";
116+
let logsUpdateInterval: NodeJS.Timeout | undefined;
117+
const messages = [
118+
{level: "debug", data: "Debug-level message"},
119+
{level: "info", data: "Info-level message"},
120+
{level: "notice", data: "Notice-level message"},
121+
{level: "warning", data: "Warning-level message"},
122+
{level: "error", data: "Error-level message"},
123+
{level: "critical", data: "Critical-level message"},
124+
{level: "alert", data: "Alert level-message"},
125+
{level: "emergency", data: "Emergency-level message"}
126+
]
127+
128+
const isMessageIgnored = (level:LoggingLevel):boolean => {
129+
const currentLevel = messages.findIndex((msg) => logLevel === msg.level);
130+
const messageLevel = messages.findIndex((msg) => level === msg.level);
131+
return messageLevel < currentLevel;
132+
}
133+
134+
// Set up update interval for random log messages
135+
logsUpdateInterval = setInterval(() => {
136+
let message = {
137+
method: "notifications/message",
138+
params: messages[Math.floor(Math.random() * messages.length)],
139+
}
140+
if (!isMessageIgnored(message.params.level as LoggingLevel)) server.notification(message);
141+
}, 15000);
142+
114143
// Helper method to request sampling from client
115144
const requestSampling = async (
116145
context: string,
@@ -451,7 +480,7 @@ export const createServer = () => {
451480

452481
if (name === ToolName.ANNOTATED_MESSAGE) {
453482
const { messageType, includeImage } = AnnotatedMessageSchema.parse(args);
454-
483+
455484
const content = [];
456485

457486
// Main message with different priorities/audiences based on type
@@ -511,7 +540,7 @@ export const createServer = () => {
511540
if (!resourceId) return { completion: { values: [] } };
512541

513542
// Filter resource IDs that start with the input value
514-
const values = EXAMPLE_COMPLETIONS.resourceId.filter(id =>
543+
const values = EXAMPLE_COMPLETIONS.resourceId.filter(id =>
515544
id.startsWith(argument.value)
516545
);
517546
return { completion: { values, hasMore: false, total: values.length } };
@@ -522,7 +551,7 @@ export const createServer = () => {
522551
const completions = EXAMPLE_COMPLETIONS[argument.name as keyof typeof EXAMPLE_COMPLETIONS];
523552
if (!completions) return { completion: { values: [] } };
524553

525-
const values = completions.filter(value =>
554+
const values = completions.filter(value =>
526555
value.startsWith(argument.value)
527556
);
528557
return { completion: { values, hasMore: false, total: values.length } };
@@ -533,24 +562,24 @@ export const createServer = () => {
533562

534563
server.setRequestHandler(SetLevelRequestSchema, async (request) => {
535564
const { level } = request.params;
565+
logLevel = level;
536566

537567
// Demonstrate different log levels
538568
await server.notification({
539569
method: "notifications/message",
540570
params: {
541571
level: "debug",
542572
logger: "test-server",
543-
data: `Logging level set to: ${level}`,
573+
data: `Logging level set to: ${logLevel}`,
544574
},
545575
});
546576

547577
return {};
548578
});
549579

550580
const cleanup = async () => {
551-
if (updateInterval) {
552-
clearInterval(updateInterval);
553-
}
581+
if (subsUpdateInterval) clearInterval(subsUpdateInterval);
582+
if (logsUpdateInterval) clearInterval(logsUpdateInterval);
554583
};
555584

556585
return { server, cleanup };

src/fetch/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ ModelContextProtocol/1.0 (User-Specified; +https://github.com/modelcontextprotoc
107107

108108
This can be customized by adding the argument `--user-agent=YourUserAgent` to the `args` list in the configuration.
109109

110+
### Customization - Proxy
111+
112+
The server can be configured to use a proxy by using the `--proxy-url` argument.
113+
110114
## Debugging
111115

112116
You can use the MCP inspector to debug the server. For uvx installations:

src/fetch/pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "mcp-server-fetch"
3-
version = "0.6.2"
3+
version = "0.6.3"
44
description = "A Model Context Protocol server providing tools to fetch and convert web content for usage by LLMs"
55
readme = "README.md"
66
requires-python = ">=3.10"
@@ -16,6 +16,7 @@ classifiers = [
1616
"Programming Language :: Python :: 3.10",
1717
]
1818
dependencies = [
19+
"httpx<0.28",
1920
"markdownify>=0.13.1",
2021
"mcp>=1.1.3",
2122
"protego>=0.3.1",

src/fetch/src/mcp_server_fetch/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ def main():
1515
action="store_true",
1616
help="Ignore robots.txt restrictions",
1717
)
18+
parser.add_argument("--proxy-url", type=str, help="Proxy URL to use for requests")
1819

1920
args = parser.parse_args()
20-
asyncio.run(serve(args.user_agent, args.ignore_robots_txt))
21+
asyncio.run(serve(args.user_agent, args.ignore_robots_txt, args.proxy_url))
2122

2223

2324
if __name__ == "__main__":

src/fetch/src/mcp_server_fetch/server.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def get_robots_txt_url(url: str) -> str:
6363
return robots_url
6464

6565

66-
async def check_may_autonomously_fetch_url(url: str, user_agent: str) -> None:
66+
async def check_may_autonomously_fetch_url(url: str, user_agent: str, proxy_url: str | None = None) -> None:
6767
"""
6868
Check if the URL can be fetched by the user agent according to the robots.txt file.
6969
Raises a McpError if not.
@@ -72,7 +72,7 @@ async def check_may_autonomously_fetch_url(url: str, user_agent: str) -> None:
7272

7373
robot_txt_url = get_robots_txt_url(url)
7474

75-
async with AsyncClient() as client:
75+
async with AsyncClient(proxies=proxy_url) as client:
7676
try:
7777
response = await client.get(
7878
robot_txt_url,
@@ -109,14 +109,14 @@ async def check_may_autonomously_fetch_url(url: str, user_agent: str) -> None:
109109

110110

111111
async def fetch_url(
112-
url: str, user_agent: str, force_raw: bool = False
112+
url: str, user_agent: str, force_raw: bool = False, proxy_url: str | None = None
113113
) -> Tuple[str, str]:
114114
"""
115115
Fetch the URL and return the content in a form ready for the LLM, as well as a prefix string with status information.
116116
"""
117117
from httpx import AsyncClient, HTTPError
118118

119-
async with AsyncClient() as client:
119+
async with AsyncClient(proxies=proxy_url) as client:
120120
try:
121121
response = await client.get(
122122
url,
@@ -173,19 +173,22 @@ class Fetch(BaseModel):
173173
bool,
174174
Field(
175175
default=False,
176-
description="Get the actual HTML content if the requested page, without simplification.",
176+
description="Get the actual HTML content of the requested page, without simplification.",
177177
),
178178
]
179179

180180

181181
async def serve(
182-
custom_user_agent: str | None = None, ignore_robots_txt: bool = False
182+
custom_user_agent: str | None = None,
183+
ignore_robots_txt: bool = False,
184+
proxy_url: str | None = None,
183185
) -> None:
184186
"""Run the fetch MCP server.
185187
186188
Args:
187189
custom_user_agent: Optional custom User-Agent string to use for requests
188190
ignore_robots_txt: Whether to ignore robots.txt restrictions
191+
proxy_url: Optional proxy URL to use for requests
189192
"""
190193
server = Server("mcp-fetch")
191194
user_agent_autonomous = custom_user_agent or DEFAULT_USER_AGENT_AUTONOMOUS
@@ -229,10 +232,10 @@ async def call_tool(name, arguments: dict) -> list[TextContent]:
229232
raise McpError(ErrorData(code=INVALID_PARAMS, message="URL is required"))
230233

231234
if not ignore_robots_txt:
232-
await check_may_autonomously_fetch_url(url, user_agent_autonomous)
235+
await check_may_autonomously_fetch_url(url, user_agent_autonomous, proxy_url)
233236

234237
content, prefix = await fetch_url(
235-
url, user_agent_autonomous, force_raw=args.raw
238+
url, user_agent_autonomous, force_raw=args.raw, proxy_url=proxy_url
236239
)
237240
original_length = len(content)
238241
if args.start_index >= original_length:
@@ -259,7 +262,7 @@ async def get_prompt(name: str, arguments: dict | None) -> GetPromptResult:
259262
url = arguments["url"]
260263

261264
try:
262-
content, prefix = await fetch_url(url, user_agent_manual)
265+
content, prefix = await fetch_url(url, user_agent_manual, proxy_url=proxy_url)
263266
# TODO: after SDK bug is addressed, don't catch the exception
264267
except McpError as e:
265268
return GetPromptResult(

src/filesystem/README.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,16 @@ Node.js server implementing Model Context Protocol (MCP) for filesystem operatio
4141
- Features:
4242
- Line-based and multi-line content matching
4343
- Whitespace normalization with indentation preservation
44-
- Fuzzy matching with confidence scoring
4544
- Multiple simultaneous edits with correct positioning
4645
- Indentation style detection and preservation
4746
- Git-style diff output with context
4847
- Preview changes with dry run mode
49-
- Failed match debugging with confidence scores
5048
- Inputs:
5149
- `path` (string): File to edit
5250
- `edits` (array): List of edit operations
5351
- `oldText` (string): Text to search for (can be substring)
5452
- `newText` (string): Text to replace with
5553
- `dryRun` (boolean): Preview changes without applying (default: false)
56-
- `options` (object): Optional formatting settings
57-
- `preserveIndentation` (boolean): Keep existing indentation (default: true)
58-
- `normalizeWhitespace` (boolean): Normalize spaces while preserving structure (default: true)
59-
- `partialMatch` (boolean): Enable fuzzy matching (default: true)
6054
- Returns detailed diff and match information for dry runs, otherwise applies changes
6155
- Best Practice: Always use dryRun first to preview changes before applying them
6256

src/filesystem/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ const allowedDirectories = args.map(dir =>
4242
// Validate that all directories exist and are accessible
4343
await Promise.all(args.map(async (dir) => {
4444
try {
45-
const stats = await fs.stat(dir);
45+
const stats = await fs.stat(expandHome(dir));
4646
if (!stats.isDirectory()) {
4747
console.error(`Error: ${dir} is not a directory`);
4848
process.exit(1);

0 commit comments

Comments
 (0)