Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
a247ff6
Add Kash.click (#2945)
paracetamol951 Nov 25, 2025
7fe5a63
Add Current Time UTC to Community MCP server list (#2873)
jairampatel Nov 25, 2025
99813ce
Update gitpython library for git mcp server (#2948)
cblecker Nov 25, 2025
fcb451a
docs: add CODEX CLI installation for sequential-thinking (#2956)
alessiopelliccione Nov 25, 2025
4914ac8
Update README.md - adding mcp resource (#3055)
eyal0s Nov 25, 2025
67033f5
docs: add mcp-proxy (mikluko) to community servers (#3050)
mikluko Nov 25, 2025
cecaad4
Add PGYER MCP Server information to README (#3048)
shishirui Nov 25, 2025
8b7acb6
Add Appium MCP Server entry to README (#3039)
SrinivasanTarget Nov 25, 2025
e194ed4
feat: add uuv mcp to official server (#3036)
luifr10 Nov 25, 2025
d00e2a0
Add Airwallex Developer MCP to Official Integrations (#3035)
amar-awx Nov 25, 2025
e3372c7
Add Notion MCP server fork to README (#3032)
njbrake Nov 25, 2025
1ab85fa
Add PrestaShop MCP server information to README (#3030)
cnavarro-prestashop Nov 25, 2025
f221ed0
Add Fabi to Community Servers (#3026)
ltang Nov 25, 2025
d080272
Add MCP Elastic Email to Third Party Servers (#3025)
eeppodolak Nov 25, 2025
3ed4445
Add predictive maintenance MCP project to README (#3000)
LGDiMaggio Nov 25, 2025
902eed6
fix(sequential-thinking): Fix a typo for `nextThoughtNeeded` (#3010)
koic Nov 25, 2025
82f5ab1
Update README.md: Add MemOS API Service MCP to community servers (#3024)
endxxxx Nov 25, 2025
1e1fda5
Add Destinia MCP Server (#3022)
darode Nov 25, 2025
5c06dbb
Add DeployHQ integration details to README (#2984)
facundofarias Nov 25, 2025
a789dcb
Add Large File MCP server to community servers (#2979)
willianpinho Nov 25, 2025
8c9a83a
Add listing for Stardog MCP server (#2980)
pranav-k Nov 25, 2025
0505256
Add Diffusion MCP Server to official integrations (#2985)
paddywalsh Nov 25, 2025
92e9676
HTTP 402 implementation on Solana in an MCP server (#2986)
paracetamol951 Nov 25, 2025
7b3c569
Add Success.co to readme.md (#2991)
irlTopper Nov 25, 2025
8916262
add kubeflow spark mcp server (#2987)
nabuskey Nov 25, 2025
949314f
Add Context Processor to Third-Party Servers (#3062)
mschultheiss83 Nov 25, 2025
816fbbe
feat: Added Withings MCP server for health data analysis (#3011)
akutishevsky Nov 25, 2025
78496bf
docs: add Clix MCP Server to Official Integrations (#2999)
hohieuai Nov 25, 2025
fa3cbb5
docs(README): add Notifly MCP Server to Official Integrations (#2998)
hohieuai Nov 25, 2025
ac19dd7
Add Runbear MCP client to README.md (#2994)
ssowonny Nov 25, 2025
8e804d3
Add HOPX MCP server to official integrations (#2993)
alexo-bunnyshell Nov 25, 2025
068e285
Add Plus AI MCP server description to README (#2992)
zfine87 Nov 25, 2025
fe67174
Add NetApp MCP server link (#2978)
mboglesby Nov 25, 2025
db46aaa
Add Arr Suite and Restream MCP servers to community registry (#2977)
shaktech786 Nov 25, 2025
9758f34
chore(uv): Prefer `--locked` for dependency sync (#1852)
eugengi Nov 25, 2025
2f93d3c
Add comprehensive tests for git server functions (#2970)
UmakanthKaspa Nov 25, 2025
8e0c890
Add Wekan MCP server to README (#2966)
namar0x0309 Nov 25, 2025
76dcff0
Fix a lifecycle bug with server everything in stdio mode (#2848)
nick-pape Nov 25, 2025
78e0088
fix(fetch): update uv.lock to sync with pyproject.toml
domdomegg Nov 25, 2025
350ffee
Fix VS Code MCP documentation URLs
domdomegg Nov 25, 2025
80a16e5
Merge pull request #3066 from modelcontextprotocol/adamj/fix-vscode-m…
olaservo Nov 26, 2025
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
2 changes: 1 addition & 1 deletion .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ jobs:

- name: Install dependencies
working-directory: src/${{ matrix.package }}
run: uv sync --frozen --all-extras --dev
run: uv sync --locked --all-extras --dev

- name: Run pyright
working-directory: src/${{ matrix.package }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ jobs:

- name: Install dependencies
working-directory: src/${{ matrix.package }}
run: uv sync --frozen --all-extras --dev
run: uv sync --locked --all-extras --dev

- name: Run pyright
working-directory: src/${{ matrix.package }}
Expand Down
37 changes: 36 additions & 1 deletion README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/everything/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ Add the configuration to your user-level MCP configuration file. Open the Comman
**Method 2: Workspace Configuration**
Alternatively, you can add the configuration to a file called `.vscode/mcp.json` in your workspace. This will allow you to share the configuration with others.

> For more details about MCP configuration in VS Code, see the [official VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/mcp).
> For more details about MCP configuration in VS Code, see the [official VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/customization/mcp-servers).

#### NPX

Expand Down
10 changes: 7 additions & 3 deletions src/everything/stdio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ async function main() {
const transport = new StdioServerTransport();
const {server, cleanup, startNotificationIntervals} = createServer();

// Cleanup when client disconnects
server.onclose = async () => {
await cleanup();
process.exit(0);
};

await server.connect(transport);
startNotificationIntervals();

// Cleanup on exit
process.on("SIGINT", async () => {
await cleanup();
await server.close();
process.exit(0);
await server.close();
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/fetch/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ ENV UV_LINK_MODE=copy
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project --no-dev --no-editable
uv sync --locked --no-install-project --no-dev --no-editable

# Then, add the rest of the project source code and install it
# Installing separately from its dependencies allows optimal layer caching
ADD . /app
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev --no-editable
uv sync --locked --no-dev --no-editable

FROM python:3.12-slim-bookworm

Expand Down
691 changes: 347 additions & 344 deletions src/fetch/uv.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/filesystem/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,7 @@ Add the configuration to your user-level MCP configuration file. Open the Comman
**Method 2: Workspace Configuration**
Alternatively, you can add the configuration to a file called `.vscode/mcp.json` in your workspace. This will allow you to share the configuration with others.

> For more details about MCP configuration in VS Code, see the [official VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/mcp).
> For more details about MCP configuration in VS Code, see the [official VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/customization/mcp-servers).

You can provide sandboxed directories to the server by mounting them to `/projects`. Adding the `ro` flag will make the directory readonly by the server.

Expand Down
4 changes: 2 additions & 2 deletions src/git/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ ENV UV_LINK_MODE=copy
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=uv.lock,target=uv.lock \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
uv sync --frozen --no-install-project --no-dev --no-editable
uv sync --locked --no-install-project --no-dev --no-editable

# Then, add the rest of the project source code and install it
# Installing separately from its dependencies allows optimal layer caching
ADD . /app
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --frozen --no-dev --no-editable
uv sync --locked --no-dev --no-editable

FROM python:3.12-slim-bookworm

Expand Down
2 changes: 1 addition & 1 deletion src/git/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ Add the configuration to your user-level MCP configuration file. Open the Comman
**Method 2: Workspace Configuration**
Alternatively, you can add the configuration to a file called `.vscode/mcp.json` in your workspace. This will allow you to share the configuration with others.

> For more details about MCP configuration in VS Code, see the [official VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/mcp).
> For more details about MCP configuration in VS Code, see the [official VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/customization/mcp-servers).

```json
{
Expand Down
6 changes: 3 additions & 3 deletions src/git/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ classifiers = [
]
dependencies = [
"click>=8.1.7",
"gitpython>=3.1.43",
"gitpython>=3.1.45",
"mcp>=1.0.0",
"pydantic>=2.0.0",
]
Expand All @@ -29,8 +29,8 @@ mcp-server-git = "mcp_server_git:main"
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.uv]
dev-dependencies = ["pyright>=1.1.389", "ruff>=0.7.3", "pytest>=8.0.0"]
[dependency-groups]
dev = ["pyright>=1.1.407", "ruff>=0.7.3", "pytest>=8.0.0"]

[tool.pytest.ini_options]
testpaths = ["tests"]
Expand Down
19 changes: 12 additions & 7 deletions src/git/src/mcp_server_git/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,9 @@ def git_log(repo: git.Repo, max_count: int = 10, start_timestamp: Optional[str]
if end_timestamp:
args.extend(['--until', end_timestamp])
args.extend(['--format=%H%n%an%n%ad%n%s%n'])

log_output = repo.git.log(*args).split('\n')

log = []
# Process commits in groups of 4 (hash, author, date, message)
for i in range(0, len(log_output), 4):
Expand Down Expand Up @@ -199,7 +199,12 @@ def git_show(repo: git.Repo, revision: str) -> str:
diff = commit.diff(git.NULL_TREE, create_patch=True)
for d in diff:
output.append(f"\n--- {d.a_path}\n+++ {d.b_path}\n")
output.append(d.diff.decode('utf-8'))
if d.diff is None:
continue
if isinstance(d.diff, bytes):
output.append(d.diff.decode('utf-8'))
else:
output.append(d.diff)
return "".join(output)

def git_branch(repo: git.Repo, branch_type: str, contains: str | None = None, not_contains: str | None = None) -> str:
Expand Down Expand Up @@ -343,7 +348,7 @@ def by_commandline() -> Sequence[str]:
@server.call_tool()
async def call_tool(name: str, arguments: dict) -> list[TextContent]:
repo_path = Path(arguments["repo_path"])

# For all commands, we need an existing repo
repo = git.Repo(repo_path)

Expand Down Expand Up @@ -400,7 +405,7 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
# Update the LOG case:
case GitTools.LOG:
log = git_log(
repo,
repo,
arguments.get("max_count", 10),
arguments.get("start_timestamp"),
arguments.get("end_timestamp")
Expand All @@ -409,7 +414,7 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
type="text",
text="Commit history:\n" + "\n".join(log)
)]

case GitTools.CREATE_BRANCH:
result = git_create_branch(
repo,
Expand Down Expand Up @@ -446,7 +451,7 @@ async def call_tool(name: str, arguments: dict) -> list[TextContent]:
type="text",
text=result
)]

case _:
raise ValueError(f"Unknown tool: {name}")

Expand Down
163 changes: 156 additions & 7 deletions src/git/tests/test_server.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
import pytest
from pathlib import Path
import git
from mcp_server_git.server import git_checkout, git_branch, git_add, git_status
from mcp_server_git.server import (
git_checkout,
git_branch,
git_add,
git_status,
git_diff_unstaged,
git_diff_staged,
git_diff,
git_commit,
git_reset,
git_log,
git_create_branch,
git_show
)
import shutil

@pytest.fixture
Expand Down Expand Up @@ -35,8 +48,6 @@ def test_git_branch_local(test_repository):
assert "new-branch-local" in result

def test_git_branch_remote(test_repository):
# GitPython does not easily support creating remote branches without a remote.
# This test will check the behavior when 'remote' is specified without actual remotes.
result = git_branch(test_repository, "remote")
assert "" == result.strip() # Should be empty if no remote branches

Expand All @@ -46,28 +57,32 @@ def test_git_branch_all(test_repository):
assert "new-branch-all" in result

def test_git_branch_contains(test_repository):
# Get the default branch name (could be "main" or "master")
default_branch = test_repository.active_branch.name
# Create a new branch and commit to it
test_repository.git.checkout("-b", "feature-branch")
Path(test_repository.working_dir / Path("feature.txt")).write_text("feature content")
test_repository.index.add(["feature.txt"])
commit = test_repository.index.commit("feature commit")
test_repository.git.checkout("master")
test_repository.git.checkout(default_branch)

result = git_branch(test_repository, "local", contains=commit.hexsha)
assert "feature-branch" in result
assert "master" not in result
assert default_branch not in result

def test_git_branch_not_contains(test_repository):
# Get the default branch name (could be "main" or "master")
default_branch = test_repository.active_branch.name
# Create a new branch and commit to it
test_repository.git.checkout("-b", "another-feature-branch")
Path(test_repository.working_dir / Path("another_feature.txt")).write_text("another feature content")
test_repository.index.add(["another_feature.txt"])
commit = test_repository.index.commit("another feature commit")
test_repository.git.checkout("master")
test_repository.git.checkout(default_branch)

result = git_branch(test_repository, "local", not_contains=commit.hexsha)
assert "another-feature-branch" not in result
assert "master" in result
assert default_branch in result

def test_git_add_all_files(test_repository):
file_path = Path(test_repository.working_dir) / "all_file.txt"
Expand Down Expand Up @@ -97,3 +112,137 @@ def test_git_status(test_repository):

assert result is not None
assert "On branch" in result or "branch" in result.lower()

def test_git_diff_unstaged(test_repository):
file_path = Path(test_repository.working_dir) / "test.txt"
file_path.write_text("modified content")

result = git_diff_unstaged(test_repository)

assert "test.txt" in result
assert "modified content" in result

def test_git_diff_unstaged_empty(test_repository):
result = git_diff_unstaged(test_repository)

assert result == ""

def test_git_diff_staged(test_repository):
file_path = Path(test_repository.working_dir) / "staged_file.txt"
file_path.write_text("staged content")
test_repository.index.add(["staged_file.txt"])

result = git_diff_staged(test_repository)

assert "staged_file.txt" in result
assert "staged content" in result

def test_git_diff_staged_empty(test_repository):
result = git_diff_staged(test_repository)

assert result == ""

def test_git_diff(test_repository):
test_repository.git.checkout("-b", "feature-diff")
file_path = Path(test_repository.working_dir) / "test.txt"
file_path.write_text("feature changes")
test_repository.index.add(["test.txt"])
test_repository.index.commit("feature commit")

result = git_diff(test_repository, "master")

assert "test.txt" in result
assert "feature changes" in result

def test_git_commit(test_repository):
file_path = Path(test_repository.working_dir) / "commit_test.txt"
file_path.write_text("content to commit")
test_repository.index.add(["commit_test.txt"])

result = git_commit(test_repository, "test commit message")

assert "Changes committed successfully with hash" in result

latest_commit = test_repository.head.commit
assert latest_commit.message.strip() == "test commit message"

def test_git_reset(test_repository):
file_path = Path(test_repository.working_dir) / "reset_test.txt"
file_path.write_text("content to reset")
test_repository.index.add(["reset_test.txt"])

staged_before = [item.a_path for item in test_repository.index.diff("HEAD")]
assert "reset_test.txt" in staged_before

result = git_reset(test_repository)

assert result == "All staged changes reset"

staged_after = [item.a_path for item in test_repository.index.diff("HEAD")]
assert "reset_test.txt" not in staged_after

def test_git_log(test_repository):
for i in range(3):
file_path = Path(test_repository.working_dir) / f"log_test_{i}.txt"
file_path.write_text(f"content {i}")
test_repository.index.add([f"log_test_{i}.txt"])
test_repository.index.commit(f"commit {i}")

result = git_log(test_repository, max_count=2)

assert isinstance(result, list)
assert len(result) == 2
assert "Commit:" in result[0]
assert "Author:" in result[0]
assert "Date:" in result[0]
assert "Message:" in result[0]

def test_git_log_default(test_repository):
result = git_log(test_repository)

assert isinstance(result, list)
assert len(result) >= 1
assert "initial commit" in result[0]

def test_git_create_branch(test_repository):
result = git_create_branch(test_repository, "new-feature-branch")

assert "Created branch 'new-feature-branch'" in result

branches = [ref.name for ref in test_repository.references]
assert "new-feature-branch" in branches

def test_git_create_branch_from_base(test_repository):
test_repository.git.checkout("-b", "base-branch")
file_path = Path(test_repository.working_dir) / "base.txt"
file_path.write_text("base content")
test_repository.index.add(["base.txt"])
test_repository.index.commit("base commit")

result = git_create_branch(test_repository, "derived-branch", "base-branch")

assert "Created branch 'derived-branch' from 'base-branch'" in result

def test_git_show(test_repository):
file_path = Path(test_repository.working_dir) / "show_test.txt"
file_path.write_text("show content")
test_repository.index.add(["show_test.txt"])
test_repository.index.commit("show test commit")

commit_sha = test_repository.head.commit.hexsha

result = git_show(test_repository, commit_sha)

assert "Commit:" in result
assert "Author:" in result
assert "show test commit" in result
assert "show_test.txt" in result

def test_git_show_initial_commit(test_repository):
initial_commit = list(test_repository.iter_commits())[-1]

result = git_show(test_repository, initial_commit.hexsha)

assert "Commit:" in result
assert "initial commit" in result
assert "test.txt" in result
Loading
Loading