Skip to content

Commit 6d13ae5

Browse files
authored
Merge branch 'main' into community/mcp-server-mysql-on-nodejs
2 parents 625247a + 0968e43 commit 6d13ae5

File tree

10 files changed

+201
-11
lines changed

10 files changed

+201
-11
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ A growing set of community-developed and maintained servers demonstrates various
7575
- **[Kubernetes](https://github.com/Flux159/mcp-server-kubernetes)** - Connect to Kubernetes cluster and manage pods, deployments, and services.
7676
- **[OpenAPI](https://github.com/snaggle-ai/openapi-mcp-server)** - Interact with [OpenAPI](https://www.openapis.org/) APIs.
7777
- **[Pandoc](https://github.com/vivekVells/mcp-pandoc)** - MCP server for seamless document format conversion using Pandoc, supporting Markdown, HTML, and plain text, with other formats like PDF, csv and docx in development.
78+
- **[HuggingFace Spaces](https://github.com/evalstate/mcp-hfspace)** - Server for using HuggingFace Spaces, supporting Open Source Image, Audio, Text Models and more. Claude Desktop mode for easy integration.
7879

7980
## 📚 Resources
8081

package-lock.json

Lines changed: 25 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/filesystem/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ Node.js server implementing Model Context Protocol (MCP) for filesystem operatio
8282
- Inputs:
8383
- `path` (string): Starting directory
8484
- `pattern` (string): Search pattern
85+
- `excludePatterns` (string[]): Exclude any patterns. Glob formats are supported.
8586
- Case-insensitive matching
8687
- Returns full paths to matches
8788

src/filesystem/index.ts

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import os from 'os';
1313
import { z } from "zod";
1414
import { zodToJsonSchema } from "zod-to-json-schema";
1515
import { diffLines, createTwoFilesPatch } from 'diff';
16+
import { minimatch } from 'minimatch';
1617

1718
// Command line argument parsing
1819
const args = process.argv.slice(2);
@@ -134,6 +135,7 @@ const MoveFileArgsSchema = z.object({
134135
const SearchFilesArgsSchema = z.object({
135136
path: z.string(),
136137
pattern: z.string(),
138+
excludePatterns: z.array(z.string()).optional().default([])
137139
});
138140

139141
const GetFileInfoArgsSchema = z.object({
@@ -183,6 +185,7 @@ async function getFileStats(filePath: string): Promise<FileInfo> {
183185
async function searchFiles(
184186
rootPath: string,
185187
pattern: string,
188+
excludePatterns: string[] = []
186189
): Promise<string[]> {
187190
const results: string[] = [];
188191

@@ -191,11 +194,22 @@ async function searchFiles(
191194

192195
for (const entry of entries) {
193196
const fullPath = path.join(currentPath, entry.name);
194-
197+
195198
try {
196199
// Validate each path before processing
197200
await validatePath(fullPath);
198201

202+
// Check if path matches any exclude pattern
203+
const relativePath = path.relative(rootPath, fullPath);
204+
const shouldExclude = excludePatterns.some(pattern => {
205+
const globPattern = pattern.includes('*') ? pattern : `**/${pattern}/**`;
206+
return minimatch(relativePath, globPattern, { dot: true });
207+
});
208+
209+
if (shouldExclude) {
210+
continue;
211+
}
212+
199213
if (entry.name.toLowerCase().includes(pattern.toLowerCase())) {
200214
results.push(fullPath);
201215
}
@@ -522,7 +536,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
522536
throw new Error(`Invalid arguments for search_files: ${parsed.error}`);
523537
}
524538
const validPath = await validatePath(parsed.data.path);
525-
const results = await searchFiles(validPath, parsed.data.pattern);
539+
const results = await searchFiles(validPath, parsed.data.pattern, parsed.data.excludePatterns);
526540
return {
527541
content: [{ type: "text", text: results.length > 0 ? results.join("\n") : "No matches found" }],
528542
};
@@ -544,9 +558,9 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
544558

545559
case "list_allowed_directories": {
546560
return {
547-
content: [{
548-
type: "text",
549-
text: `Allowed directories:\n${allowedDirectories.join('\n')}`
561+
content: [{
562+
type: "text",
563+
text: `Allowed directories:\n${allowedDirectories.join('\n')}`
550564
}],
551565
};
552566
}
@@ -574,4 +588,4 @@ async function runServer() {
574588
runServer().catch((error) => {
575589
console.error("Fatal error running server:", error);
576590
process.exit(1);
577-
});
591+
});

src/filesystem/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@
2222
"@modelcontextprotocol/sdk": "0.5.0",
2323
"diff": "^5.1.0",
2424
"glob": "^10.3.10",
25+
"minimatch": "^10.0.1",
2526
"zod-to-json-schema": "^3.23.5"
2627
},
2728
"devDependencies": {
2829
"@types/diff": "^5.0.9",
30+
"@types/minimatch": "^5.1.2",
2931
"@types/node": "^20.11.0",
3032
"shx": "^0.3.4",
3133
"typescript": "^5.3.3"
3234
}
33-
}
35+
}

src/git/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ Please note that mcp-server-git is currently in early development. The functiona
6767
- `branch_name` (string): Name of the new branch
6868
- `start_point` (string, optional): Starting point for the new branch
6969
- Returns: Confirmation of branch creation
70+
8. `git_checkout`
71+
- Switches branches
72+
- Inputs:
73+
- `repo_path` (string): Path to Git repository
74+
- `branch_name` (string): Name of branch to checkout
75+
- Returns: Confirmation of branch switch
7076

7177
## Installation
7278

src/git/pyproject.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,10 @@ requires = ["hatchling"]
3030
build-backend = "hatchling.build"
3131

3232
[tool.uv]
33-
dev-dependencies = ["pyright>=1.1.389", "ruff>=0.7.3"]
33+
dev-dependencies = ["pyright>=1.1.389", "ruff>=0.7.3", "pytest>=8.0.0"]
34+
35+
[tool.pytest.ini_options]
36+
testpaths = ["tests"]
37+
python_files = "test_*.py"
38+
python_classes = "Test*"
39+
python_functions = "test_*"

src/git/src/mcp_server_git/server.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ class GitCreateBranch(BaseModel):
4848
branch_name: str
4949
base_branch: str | None = None
5050

51+
class GitCheckout(BaseModel):
52+
repo_path: str
53+
branch_name: str
54+
5155
class GitTools(str, Enum):
5256
STATUS = "git_status"
5357
DIFF_UNSTAGED = "git_diff_unstaged"
@@ -58,6 +62,7 @@ class GitTools(str, Enum):
5862
RESET = "git_reset"
5963
LOG = "git_log"
6064
CREATE_BRANCH = "git_create_branch"
65+
CHECKOUT = "git_checkout"
6166

6267
def git_status(repo: git.Repo) -> str:
6368
return repo.git.status()
@@ -104,6 +109,10 @@ def git_create_branch(repo: git.Repo, branch_name: str, base_branch: str | None
104109
repo.create_head(branch_name, base)
105110
return f"Created branch '{branch_name}' from '{base.name}'"
106111

112+
def git_checkout(repo: git.Repo, branch_name: str) -> str:
113+
repo.git.checkout(branch_name)
114+
return f"Switched to branch '{branch_name}'"
115+
107116
async def serve(repository: Path | None) -> None:
108117
logger = logging.getLogger(__name__)
109118

@@ -165,6 +174,11 @@ async def list_tools() -> list[Tool]:
165174
description="Creates a new branch from an optional base branch",
166175
inputSchema=GitCreateBranch.schema(),
167176
),
177+
Tool(
178+
name=GitTools.CHECKOUT,
179+
description="Switches branches",
180+
inputSchema=GitCheckout.schema(),
181+
),
168182
]
169183

170184
async def list_repos() -> Sequence[str]:
@@ -269,6 +283,13 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
269283
text=result
270284
)]
271285

286+
case GitTools.CHECKOUT:
287+
result = git_checkout(repo, arguments["branch_name"])
288+
return [TextContent(
289+
type="text",
290+
text=result
291+
)]
292+
272293
case _:
273294
raise ValueError(f"Unknown tool: {name}")
274295

src/git/tests/test_server.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import pytest
2+
from pathlib import Path
3+
import git
4+
from mcp_server_git.server import git_checkout
5+
import shutil
6+
7+
@pytest.fixture
8+
def test_repository(tmp_path: Path):
9+
repo_path = tmp_path / "temp_test_repo"
10+
test_repo = git.Repo.init(repo_path)
11+
12+
Path(repo_path / "test.txt").write_text("test")
13+
test_repo.index.add(["test.txt"])
14+
test_repo.index.commit("initial commit")
15+
16+
yield test_repo
17+
18+
shutil.rmtree(repo_path)
19+
20+
def test_git_checkout_existing_branch(test_repository):
21+
test_repository.git.branch("test-branch")
22+
result = git_checkout(test_repository, "test-branch")
23+
24+
assert "Switched to branch 'test-branch'" in result
25+
assert test_repository.active_branch.name == "test-branch"
26+
27+
def test_git_checkout_nonexistent_branch(test_repository):
28+
29+
with pytest.raises(git.GitCommandError):
30+
git_checkout(test_repository, "nonexistent-branch")

0 commit comments

Comments
 (0)