Merge pull request #11 from modelcontextprotocol/davidsp/git

major update to the git server
This commit is contained in:
David Soria Parra
2024-11-20 16:00:15 +00:00
committed by GitHub
7 changed files with 249 additions and 57 deletions

View File

@@ -1 +1 @@
3.11 3.10

View File

@@ -1,9 +1,13 @@
# mcp-git # mcp-git: A git MCP server
A Model Context Protocol server for Git repository interaction and automation. This server provides tools to read, search, and manipulate Git repositories via Large Language Models. A Model Context Protocol server for Git repository interaction and automation. This server provides tools to read, search, and manipulate Git repositories via Large Language Models.
Please note that mcp-git is currently in early development. The functionality and available tools are subject to change and expansion as we continue to develop and improve the server.
## Available Tools ## Available Tools
The current list of tools includes:
- `git_read_file`: Read contents of a file at a specific Git reference - `git_read_file`: Read contents of a file at a specific Git reference
- `git_list_files`: List all files in a repository or subdirectory - `git_list_files`: List all files in a repository or subdirectory
- `git_file_history`: Get commit history for a specific file - `git_file_history`: Get commit history for a specific file
@@ -12,10 +16,15 @@ A Model Context Protocol server for Git repository interaction and automation. T
- `git_get_diff`: View diffs between Git references - `git_get_diff`: View diffs between Git references
- `git_get_repo_structure`: View repository file structure - `git_get_repo_structure`: View repository file structure
- `git_list_repos`: List available Git repositories - `git_list_repos`: List available Git repositories
- `git_log`: Retrieve commit log for the repository
- `git_list_branches`: List all branches in the repository
- `git_list_tags`: List all tags in the repository
This list is expected to grow as we add more functionality to the server. We welcome contributions from the community to expand and enhance the available tools.
## Installation ## Installation
### Using uv ### Using uv (recommended)
When using [`uv`](https://docs.astral.sh/uv/) no specific installation is needed. We will When using [`uv`](https://docs.astral.sh/uv/) no specific installation is needed. We will
use [`uvx`](https://docs.astral.sh/uv/guides/tools/) to directly run *mcp-git*. use [`uvx`](https://docs.astral.sh/uv/guides/tools/) to directly run *mcp-git*.
@@ -86,7 +95,9 @@ Alternatively, if using pip installation:
## Contributing ## Contributing
We encourage contributions to help expand and improve mcp-git. Whether you want to add new tools, enhance existing functionality, or improve documentation, your input is valuable.
For examples of other MCP servers and implementation patterns, see: For examples of other MCP servers and implementation patterns, see:
https://github.com/modelcontextprotocol/servers https://github.com/modelcontextprotocol/servers
Pull requests welcome! Pull requests are welcome! Feel free to contribute new ideas, bug fixes, or enhancements to make mcp-git even more powerful and useful.

View File

@@ -1,9 +1,9 @@
[project] [project]
name = "mcp-git" name = "mcp-git"
version = "0.1.0" version = "0.2.0"
description = "A Model Context Protocol server providing tools to read, search, and manipulate Git repositories programmatically via LLMs" description = "A Model Context Protocol server providing tools to read, search, and manipulate Git repositories programmatically via LLMs"
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.10"
authors = [{ name = "Anthropic, PBC." }] authors = [{ name = "Anthropic, PBC." }]
maintainers = [{ name = "David Soria Parra", email = "davidsp@anthropic.com" }] maintainers = [{ name = "David Soria Parra", email = "davidsp@anthropic.com" }]
keywords = ["git", "mcp", "llm", "automation"] keywords = ["git", "mcp", "llm", "automation"]
@@ -13,7 +13,7 @@ classifiers = [
"Intended Audience :: Developers", "Intended Audience :: Developers",
"License :: OSI Approved :: MIT License", "License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.10",
] ]
dependencies = [ dependencies = [
"click>=8.1.7", "click>=8.1.7",
@@ -30,5 +30,4 @@ requires = ["hatchling"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
[tool.uv] [tool.uv]
index-strategy = "unsafe-best-match"
dev-dependencies = ["ruff>=0.7.3"] dev-dependencies = ["ruff>=0.7.3"]

View File

@@ -1,4 +1,6 @@
import logging import logging
import json
import sys
import click import click
import anyio import anyio
import anyio.lowlevel import anyio.lowlevel
@@ -7,8 +9,16 @@ from git.types import Sequence
from mcp.server import Server from mcp.server import Server
from mcp.server.session import ServerSession from mcp.server.session import ServerSession
from mcp.server.stdio import stdio_server from mcp.server.stdio import stdio_server
from mcp.types import TextContent, Tool, EmbeddedResource, ImageContent, ListRootsResult from mcp.types import (
from enum import StrEnum ClientCapabilities,
TextContent,
Tool,
EmbeddedResource,
ImageContent,
ListRootsResult,
RootsCapability,
)
from enum import Enum
import git import git
from git.objects import Blob, Tree from git.objects import Blob, Tree
@@ -66,7 +76,21 @@ class ListReposInput(BaseModel):
pass pass
class GitTools(StrEnum): class GitLogInput(BaseModel):
repo_path: str
max_count: int = 10
ref: str = "HEAD"
class ListBranchesInput(BaseModel):
repo_path: str
class ListTagsInput(BaseModel):
repo_path: str
class GitTools(str, Enum):
READ_FILE = "git_read_file" READ_FILE = "git_read_file"
LIST_FILES = "git_list_files" LIST_FILES = "git_list_files"
FILE_HISTORY = "git_file_history" FILE_HISTORY = "git_file_history"
@@ -75,12 +99,19 @@ class GitTools(StrEnum):
GET_DIFF = "git_get_diff" GET_DIFF = "git_get_diff"
GET_REPO_STRUCTURE = "git_get_repo_structure" GET_REPO_STRUCTURE = "git_get_repo_structure"
LIST_REPOS = "git_list_repos" LIST_REPOS = "git_list_repos"
GIT_LOG = "git_log"
LIST_BRANCHES = "git_list_branches"
LIST_TAGS = "git_list_tags"
def git_read_file(repo: git.Repo, file_path: str, ref: str = "HEAD") -> str: def git_read_file(repo: git.Repo, file_path: str, ref: str = "HEAD") -> str:
tree = repo.commit(ref).tree tree = repo.commit(ref).tree
blob = tree / file_path blob = tree / file_path
try:
return blob.data_stream.read().decode("utf-8", errors="replace") return blob.data_stream.read().decode("utf-8", errors="replace")
except UnicodeDecodeError:
# If it's a binary file, return a message indicating that
return "[Binary file content not shown]"
def git_list_files(repo: git.Repo, path: str = "", ref: str = "HEAD") -> Sequence[str]: def git_list_files(repo: git.Repo, path: str = "", ref: str = "HEAD") -> Sequence[str]:
@@ -122,10 +153,14 @@ def git_search_code(
tree = repo.commit(ref).tree tree = repo.commit(ref).tree
for blob in tree.traverse(): for blob in tree.traverse():
if isinstance(blob, Blob) and Path(blob.path).match(file_pattern): if isinstance(blob, Blob) and Path(blob.path).match(file_pattern):
content = blob.data_stream.read().decode("utf-8") try:
content = blob.data_stream.read().decode("utf-8", errors="replace")
for i, line in enumerate(content.splitlines()): for i, line in enumerate(content.splitlines()):
if query in line: if query in line:
results.append(f"{blob.path}:{i+1}: {line}") results.append(f"{blob.path}:{i+1}: {line}")
except UnicodeDecodeError:
# Skip binary files
continue
return results return results
@@ -153,14 +188,35 @@ def git_get_repo_structure(repo: git.Repo, ref: str = "HEAD") -> str:
return str(structure) return str(structure)
def git_log(repo: git.Repo, max_count: int = 10, ref: str = "HEAD") -> list[str]:
commits = list(repo.iter_commits(ref, max_count=max_count))
log = []
for commit in commits:
log.append(
f"Commit: {commit.hexsha}\n"
f"Author: {commit.author}\n"
f"Date: {commit.authored_datetime}\n"
f"Message: {commit.message}\n"
)
return log
def git_list_branches(repo: git.Repo) -> list[str]:
return [str(branch) for branch in repo.branches]
def git_list_tags(repo: git.Repo) -> list[str]:
return [str(tag) for tag in repo.tags]
async def serve(repository: Path | None) -> None: async def serve(repository: Path | None) -> None:
# Set up logging # Set up logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
if repository is not None: if repository is not None:
try: try:
git.Repo(repository) git.Repo(repository)
logger.info(f"Using repository at {repository}")
except git.InvalidGitRepositoryError: except git.InvalidGitRepositoryError:
logger.error(f"{repository} is not a valid Git repository") logger.error(f"{repository} is not a valid Git repository")
return return
@@ -234,6 +290,28 @@ async def serve(repository: Path | None) -> None:
"accessible to the current session.", "accessible to the current session.",
inputSchema=ListReposInput.schema(), inputSchema=ListReposInput.schema(),
), ),
Tool(
name=GitTools.GIT_LOG,
description="Retrieves the commit log for the repository, showing the "
"history of commits including commit hashes, authors, dates, and "
"commit messages. This tool provides an overview of the project's "
"development history.",
inputSchema=GitLogInput.schema(),
),
Tool(
name=GitTools.LIST_BRANCHES,
description="Lists all branches in the Git repository. This tool "
"provides an overview of the different lines of development in the "
"project.",
inputSchema=ListBranchesInput.schema(),
),
Tool(
name=GitTools.LIST_TAGS,
description="Lists all tags in the Git repository. This tool "
"provides an overview of the tagged versions or releases in the "
"project.",
inputSchema=ListTagsInput.schema(),
),
] ]
async def list_repos() -> Sequence[str]: async def list_repos() -> Sequence[str]:
@@ -243,7 +321,14 @@ async def serve(repository: Path | None) -> None:
"server.request_context.session must be a ServerSession" "server.request_context.session must be a ServerSession"
) )
roots_result: ListRootsResult = await server.request_context.session.list_roots() if not server.request_context.session.check_client_capability(
ClientCapabilities(roots=RootsCapability())
):
return []
roots_result: ListRootsResult = (
await server.request_context.session.list_roots()
)
logger.debug(f"Roots result: {roots_result}") logger.debug(f"Roots result: {roots_result}")
repo_paths = [] repo_paths = []
for root in roots_result.roots: for root in roots_result.roots:
@@ -269,71 +354,123 @@ async def serve(repository: Path | None) -> None:
) -> list[TextContent | ImageContent | EmbeddedResource]: ) -> list[TextContent | ImageContent | EmbeddedResource]:
if name == GitTools.LIST_REPOS: if name == GitTools.LIST_REPOS:
result = await list_repos() result = await list_repos()
return [TextContent(type="text", text=str(r)) for r in result] logging.debug(f"repos={result}")
return [
TextContent(
type="text",
text=f"Here is some JSON that contains a list of git repositories: {json.dumps(result)}",
)
]
repo_path = Path(arguments["repo_path"]) repo_path = Path(arguments["repo_path"])
repo = git.Repo(repo_path) repo = git.Repo(repo_path)
match name: match name:
case GitTools.READ_FILE: case GitTools.READ_FILE:
content = git_read_file(
repo, arguments["file_path"], arguments.get("ref", "HEAD")
)
return [ return [
TextContent( TextContent(
type="text", type="text",
text=git_read_file( text=f"Here is some JSON that contains the contents of a file: {json.dumps({'content': content})}",
repo, arguments["file_path"], arguments.get("ref", "HEAD")
)
) )
] ]
case GitTools.LIST_FILES: case GitTools.LIST_FILES:
return [ files = git_list_files(
TextContent(type="text", text=str(f))
for f in git_list_files(
repo, arguments.get("path", ""), arguments.get("ref", "HEAD") repo, arguments.get("path", ""), arguments.get("ref", "HEAD")
) )
return [
TextContent(
type="text",
text=f"Here is some JSON that contains a list of files: {json.dumps({'files': list(files)})}",
)
] ]
case GitTools.FILE_HISTORY: case GitTools.FILE_HISTORY:
return [ history = git_file_history(
TextContent(type="text", text=entry)
for entry in git_file_history(
repo, arguments["file_path"], arguments.get("max_entries", 10) repo, arguments["file_path"], arguments.get("max_entries", 10)
) )
return [
TextContent(
type="text",
text=f"Here is some JSON that contains a file's history: {json.dumps({'history': list(history)})}",
)
] ]
case GitTools.COMMIT: case GitTools.COMMIT:
result = git_commit(repo, arguments["message"], arguments.get("files")) result = git_commit(repo, arguments["message"], arguments.get("files"))
return [TextContent(type="text", text=result)] return [
TextContent(
type="text",
text=f"Here is some JSON that contains the commit result: {json.dumps({'result': result})}",
)
]
case GitTools.SEARCH_CODE: case GitTools.SEARCH_CODE:
return [ results = git_search_code(
TextContent(type="text", text=result)
for result in git_search_code(
repo, repo,
arguments["query"], arguments["query"],
arguments.get("file_pattern", "*"), arguments.get("file_pattern", "*"),
arguments.get("ref", "HEAD"), arguments.get("ref", "HEAD"),
) )
]
case GitTools.GET_DIFF:
return [ return [
TextContent( TextContent(
type="text", type="text",
text=git_get_diff( text=f"Here is some JSON that contains code search matches: {json.dumps({'matches': results})}",
)
]
case GitTools.GET_DIFF:
diff = git_get_diff(
repo, repo,
arguments["ref1"], arguments["ref1"],
arguments["ref2"], arguments["ref2"],
arguments.get("file_path"), arguments.get("file_path"),
) )
return [
TextContent(
type="text",
text=f"Here is some JSON that contains a diff: {json.dumps({'diff': diff})}",
) )
] ]
case GitTools.GET_REPO_STRUCTURE: case GitTools.GET_REPO_STRUCTURE:
structure = git_get_repo_structure(repo, arguments.get("ref", "HEAD"))
return [ return [
TextContent( TextContent(
type="text", type="text",
text=git_get_repo_structure(repo, arguments.get("ref", "HEAD")) text=f"Here is some JSON that contains the repository structure: {json.dumps({'structure': structure})}",
)
]
case GitTools.GIT_LOG:
log = git_log(
repo, arguments.get("max_count", 10), arguments.get("ref", "HEAD")
)
return [
TextContent(
type="text",
text=f"Here is some JSON that contains the git log: {json.dumps({'log': log})}",
)
]
case GitTools.LIST_BRANCHES:
branches = git_list_branches(repo)
return [
TextContent(
type="text",
text=f"Here is some JSON that contains a list of branches: {json.dumps({'branches': branches})}",
)
]
case GitTools.LIST_TAGS:
tags = git_list_tags(repo)
return [
TextContent(
type="text",
text=f"Here is some JSON that contains a list of tags: {json.dumps({'tags': tags})}",
) )
] ]
@@ -348,7 +485,14 @@ async def serve(repository: Path | None) -> None:
@click.command() @click.command()
@click.option("-r", "--repository", type=click.Path(path_type=Path, dir_okay=True)) @click.option("-r", "--repository", type=click.Path(path_type=Path, dir_okay=True))
def main(repository: Path | None): @click.option("-v", "--verbose", count=True)
def main(repository: Path | None, verbose: int):
logging_level = logging.WARN
if verbose == 1:
logging_level = logging.INFO
elif verbose >= 2:
logging_level = logging.DEBUG
logging.basicConfig(level=logging_level, stream=sys.stderr)
anyio.run(serve, repository) anyio.run(serve, repository)

42
src/git/uv.lock generated
View File

@@ -1,5 +1,5 @@
version = 1 version = 1
requires-python = ">=3.11" requires-python = ">=3.10"
resolution-markers = [ resolution-markers = [
"python_full_version < '3.13'", "python_full_version < '3.13'",
"python_full_version >= '3.13'", "python_full_version >= '3.13'",
@@ -19,8 +19,10 @@ name = "anyio"
version = "4.6.2.post1" version = "4.6.2.post1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
{ name = "idna" }, { name = "idna" },
{ name = "sniffio" }, { name = "sniffio" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/9f/09/45b9b7a6d4e45c6bcb5bf61d19e3ab87df68e0601fa8c5293de3542546cc/anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c", size = 173422 } sdist = { url = "https://files.pythonhosted.org/packages/9f/09/45b9b7a6d4e45c6bcb5bf61d19e3ab87df68e0601fa8c5293de3542546cc/anyio-4.6.2.post1.tar.gz", hash = "sha256:4c8bc31ccdb51c7f7bd251f51c609e038d63e34219b44aa86e47576389880b4c", size = 173422 }
wheels = [ wheels = [
@@ -57,6 +59,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 }, { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
] ]
[[package]]
name = "exceptiongroup"
version = "1.2.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/09/35/2495c4ac46b980e4ca1f6ad6db102322ef3ad2410b79fdde159a4b0f3b92/exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc", size = 28883 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/02/cc/b7e31358aac6ed1ef2bb790a9746ac2c69bcb3c8588b41616914eb106eaf/exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b", size = 16453 },
]
[[package]] [[package]]
name = "gitdb" name = "gitdb"
version = "4.0.11" version = "4.0.11"
@@ -139,7 +150,7 @@ wheels = [
[[package]] [[package]]
name = "mcp" name = "mcp"
version = "0.9.0" version = "0.9.1"
source = { registry = "https://pypi.org/simple" } source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "anyio" }, { name = "anyio" },
@@ -149,14 +160,14 @@ dependencies = [
{ name = "sse-starlette" }, { name = "sse-starlette" },
{ name = "starlette" }, { name = "starlette" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/cd/bb/fd56a5c331a6c95a4f2ec907683db3382d30b99b808ef6f46fa4f08a4b74/mcp-0.9.0.tar.gz", hash = "sha256:1d7e3f8d78bf5b37c98a233fce8cebbb86c57d8964d2c3b03cf08cdebd103d9a", size = 78343 } sdist = { url = "https://files.pythonhosted.org/packages/e7/1c/932818470ffd49c33509110c835101a8dc4c9cdd06028b9f647fb3dde237/mcp-0.9.1.tar.gz", hash = "sha256:e8509a37c2ab546095788ed170e0fb4d7ce0cf5a3ee56b6449c78af27321a425", size = 78218 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/6f/07/077116e6a23dd0546391f5caa81b4f52938d8a81f2449c55c0b50c0215bf/mcp-0.9.0-py3-none-any.whl", hash = "sha256:e09aca08eadaf0552541aaa71271b44f99a6a5d16e5b1b03c421366f72b51753", size = 31691 }, { url = "https://files.pythonhosted.org/packages/b3/a0/2ee813d456b57a726d583868417d1ad900fbe12ee3c8cd866e3e804ca486/mcp-0.9.1-py3-none-any.whl", hash = "sha256:7f640fcfb0be486aa510594df309920ae1d375cdca1f8aff21db3a96d837f303", size = 31562 },
] ]
[[package]] [[package]]
name = "mcp-git" name = "mcp-git"
version = "0.1.0" version = "0.2.0"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },
@@ -204,6 +215,18 @@ dependencies = [
] ]
sdist = { url = "https://files.pythonhosted.org/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156 } sdist = { url = "https://files.pythonhosted.org/packages/e2/aa/6b6a9b9f8537b872f552ddd46dd3da230367754b6f707b8e1e963f515ea3/pydantic_core-2.23.4.tar.gz", hash = "sha256:2584f7cf844ac4d970fba483a717dbe10c1c1c96a969bf65d61ffe94df1b2863", size = 402156 }
wheels = [ wheels = [
{ url = "https://files.pythonhosted.org/packages/5c/8b/d3ae387f66277bd8104096d6ec0a145f4baa2966ebb2cad746c0920c9526/pydantic_core-2.23.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b10bd51f823d891193d4717448fab065733958bdb6a6b351967bd349d48d5c9b", size = 1867835 },
{ url = "https://files.pythonhosted.org/packages/46/76/f68272e4c3a7df8777798282c5e47d508274917f29992d84e1898f8908c7/pydantic_core-2.23.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4fc714bdbfb534f94034efaa6eadd74e5b93c8fa6315565a222f7b6f42ca1166", size = 1776689 },
{ url = "https://files.pythonhosted.org/packages/cc/69/5f945b4416f42ea3f3bc9d2aaec66c76084a6ff4ff27555bf9415ab43189/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63e46b3169866bd62849936de036f901a9356e36376079b05efa83caeaa02ceb", size = 1800748 },
{ url = "https://files.pythonhosted.org/packages/50/ab/891a7b0054bcc297fb02d44d05c50e68154e31788f2d9d41d0b72c89fdf7/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed1a53de42fbe34853ba90513cea21673481cd81ed1be739f7f2efb931b24916", size = 1806469 },
{ url = "https://files.pythonhosted.org/packages/31/7c/6e3fa122075d78f277a8431c4c608f061881b76c2b7faca01d317ee39b5d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cfdd16ab5e59fc31b5e906d1a3f666571abc367598e3e02c83403acabc092e07", size = 2002246 },
{ url = "https://files.pythonhosted.org/packages/ad/6f/22d5692b7ab63fc4acbc74de6ff61d185804a83160adba5e6cc6068e1128/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:255a8ef062cbf6674450e668482456abac99a5583bbafb73f9ad469540a3a232", size = 2659404 },
{ url = "https://files.pythonhosted.org/packages/11/ac/1e647dc1121c028b691028fa61a4e7477e6aeb5132628fde41dd34c1671f/pydantic_core-2.23.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a7cd62e831afe623fbb7aabbb4fe583212115b3ef38a9f6b71869ba644624a2", size = 2053940 },
{ url = "https://files.pythonhosted.org/packages/91/75/984740c17f12c3ce18b5a2fcc4bdceb785cce7df1511a4ce89bca17c7e2d/pydantic_core-2.23.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f09e2ff1f17c2b51f2bc76d1cc33da96298f0a036a137f5440ab3ec5360b624f", size = 1921437 },
{ url = "https://files.pythonhosted.org/packages/a0/74/13c5f606b64d93f0721e7768cd3e8b2102164866c207b8cd6f90bb15d24f/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e38e63e6f3d1cec5a27e0afe90a085af8b6806ee208b33030e65b6516353f1a3", size = 1966129 },
{ url = "https://files.pythonhosted.org/packages/18/03/9c4aa5919457c7b57a016c1ab513b1a926ed9b2bb7915bf8e506bf65c34b/pydantic_core-2.23.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0dbd8dbed2085ed23b5c04afa29d8fd2771674223135dc9bc937f3c09284d071", size = 2110908 },
{ url = "https://files.pythonhosted.org/packages/92/2c/053d33f029c5dc65e5cf44ff03ceeefb7cce908f8f3cca9265e7f9b540c8/pydantic_core-2.23.4-cp310-none-win32.whl", hash = "sha256:6531b7ca5f951d663c339002e91aaebda765ec7d61b7d1e3991051906ddde119", size = 1735278 },
{ url = "https://files.pythonhosted.org/packages/de/81/7dfe464eca78d76d31dd661b04b5f2036ec72ea8848dd87ab7375e185c23/pydantic_core-2.23.4-cp310-none-win_amd64.whl", hash = "sha256:7c9129eb40958b3d4500fa2467e6a83356b3b61bfff1b414c7361d9220f9ae8f", size = 1917453 },
{ url = "https://files.pythonhosted.org/packages/5d/30/890a583cd3f2be27ecf32b479d5d615710bb926d92da03e3f7838ff3e58b/pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8", size = 1865160 }, { url = "https://files.pythonhosted.org/packages/5d/30/890a583cd3f2be27ecf32b479d5d615710bb926d92da03e3f7838ff3e58b/pydantic_core-2.23.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:77733e3892bb0a7fa797826361ce8a9184d25c8dffaec60b7ffe928153680ba8", size = 1865160 },
{ url = "https://files.pythonhosted.org/packages/1d/9a/b634442e1253bc6889c87afe8bb59447f106ee042140bd57680b3b113ec7/pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d", size = 1776777 }, { url = "https://files.pythonhosted.org/packages/1d/9a/b634442e1253bc6889c87afe8bb59447f106ee042140bd57680b3b113ec7/pydantic_core-2.23.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b84d168f6c48fabd1f2027a3d1bdfe62f92cade1fb273a5d68e621da0e44e6d", size = 1776777 },
{ url = "https://files.pythonhosted.org/packages/75/9a/7816295124a6b08c24c96f9ce73085032d8bcbaf7e5a781cd41aa910c891/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e", size = 1799244 }, { url = "https://files.pythonhosted.org/packages/75/9a/7816295124a6b08c24c96f9ce73085032d8bcbaf7e5a781cd41aa910c891/pydantic_core-2.23.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df49e7a0861a8c36d089c1ed57d308623d60416dab2647a4a17fe050ba85de0e", size = 1799244 },
@@ -240,6 +263,14 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 }, { url = "https://files.pythonhosted.org/packages/16/16/b805c74b35607d24d37103007f899abc4880923b04929547ae68d478b7f4/pydantic_core-2.23.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ed541d70698978a20eb63d8c5d72f2cc6d7079d9d90f6b50bad07826f1320f5f", size = 2116814 },
{ url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 }, { url = "https://files.pythonhosted.org/packages/d1/58/5305e723d9fcdf1c5a655e6a4cc2a07128bf644ff4b1d98daf7a9dbf57da/pydantic_core-2.23.4-cp313-none-win32.whl", hash = "sha256:3d5639516376dce1940ea36edf408c554475369f5da2abd45d44621cb616f769", size = 1738360 },
{ url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 }, { url = "https://files.pythonhosted.org/packages/a5/ae/e14b0ff8b3f48e02394d8acd911376b7b66e164535687ef7dc24ea03072f/pydantic_core-2.23.4-cp313-none-win_amd64.whl", hash = "sha256:5a1504ad17ba4210df3a045132a7baeeba5a200e930f57512ee02909fc5c4cb5", size = 1919411 },
{ url = "https://files.pythonhosted.org/packages/13/a9/5d582eb3204464284611f636b55c0a7410d748ff338756323cb1ce721b96/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f455ee30a9d61d3e1a15abd5068827773d6e4dc513e795f380cdd59932c782d5", size = 1857135 },
{ url = "https://files.pythonhosted.org/packages/2c/57/faf36290933fe16717f97829eabfb1868182ac495f99cf0eda9f59687c9d/pydantic_core-2.23.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1e90d2e3bd2c3863d48525d297cd143fe541be8bbf6f579504b9712cb6b643ec", size = 1740583 },
{ url = "https://files.pythonhosted.org/packages/91/7c/d99e3513dc191c4fec363aef1bf4c8af9125d8fa53af7cb97e8babef4e40/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e203fdf807ac7e12ab59ca2bfcabb38c7cf0b33c41efeb00f8e5da1d86af480", size = 1793637 },
{ url = "https://files.pythonhosted.org/packages/29/18/812222b6d18c2d13eebbb0f7cdc170a408d9ced65794fdb86147c77e1982/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e08277a400de01bc72436a0ccd02bdf596631411f592ad985dcee21445bd0068", size = 1941963 },
{ url = "https://files.pythonhosted.org/packages/0f/36/c1f3642ac3f05e6bb4aec3ffc399fa3f84895d259cf5f0ce3054b7735c29/pydantic_core-2.23.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f220b0eea5965dec25480b6333c788fb72ce5f9129e8759ef876a1d805d00801", size = 1915332 },
{ url = "https://files.pythonhosted.org/packages/f7/ca/9c0854829311fb446020ebb540ee22509731abad886d2859c855dd29b904/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d06b0c8da4f16d1d1e352134427cb194a0a6e19ad5db9161bf32b2113409e728", size = 1957926 },
{ url = "https://files.pythonhosted.org/packages/c0/1c/7836b67c42d0cd4441fcd9fafbf6a027ad4b79b6559f80cf11f89fd83648/pydantic_core-2.23.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ba1a0996f6c2773bd83e63f18914c1de3c9dd26d55f4ac302a7efe93fb8e7433", size = 2100342 },
{ url = "https://files.pythonhosted.org/packages/a9/f9/b6bcaf874f410564a78908739c80861a171788ef4d4f76f5009656672dfe/pydantic_core-2.23.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:9a5bce9d23aac8f0cf0836ecfc033896aa8443b501c58d0602dbfd5bd5b37753", size = 1920344 },
] ]
[[package]] [[package]]
@@ -327,6 +358,7 @@ source = { registry = "https://pypi.org/simple" }
dependencies = [ dependencies = [
{ name = "click" }, { name = "click" },
{ name = "h11" }, { name = "h11" },
{ name = "typing-extensions", marker = "python_full_version < '3.11'" },
] ]
sdist = { url = "https://files.pythonhosted.org/packages/e0/fc/1d785078eefd6945f3e5bab5c076e4230698046231eb0f3747bc5c8fa992/uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e", size = 77564 } sdist = { url = "https://files.pythonhosted.org/packages/e0/fc/1d785078eefd6945f3e5bab5c076e4230698046231eb0f3747bc5c8fa992/uvicorn-0.32.0.tar.gz", hash = "sha256:f78b36b143c16f54ccdb8190d0a26b5f1901fe5a3c777e1ab29f26391af8551e", size = 77564 }
wheels = [ wheels = [

View File

@@ -1,9 +1,11 @@
from . import server from . import server
import asyncio import asyncio
def main(): def main():
"""Main entry point for the package.""" """Main entry point for the package."""
asyncio.run(server.main()) asyncio.run(server.main())
# Optionally expose other important items at package level # Optionally expose other important items at package level
__all__ = ['main', 'server'] __all__ = ["main", "server"]

View File

@@ -28,7 +28,7 @@ class McpServer(Server):
if cursor.fetchone()[0] == 0: if cursor.fetchone()[0] == 0:
cursor.execute( cursor.execute(
"INSERT INTO notes (name, content) VALUES (?, ?)", "INSERT INTO notes (name, content) VALUES (?, ?)",
("example", "This is an example note.") ("example", "This is an example note."),
) )
conn.commit() conn.commit()
@@ -55,7 +55,7 @@ class McpServer(Server):
with closing(conn.cursor()) as cursor: with closing(conn.cursor()) as cursor:
cursor.execute( cursor.execute(
"INSERT OR REPLACE INTO notes (name, content) VALUES (?, ?)", "INSERT OR REPLACE INTO notes (name, content) VALUES (?, ?)",
(name, content) (name, content),
) )
conn.commit() conn.commit()
@@ -118,10 +118,14 @@ class McpServer(Server):
"""Generate a prompt using notes from the database""" """Generate a prompt using notes from the database"""
if name != "summarize-notes": if name != "summarize-notes":
raise ValueError(f"Unknown prompt: {name}") raise ValueError(f"Unknown prompt: {name}")
notes = "<notes>\n" + "\n".join( notes = (
"<notes>\n"
+ "\n".join(
f"<note name='{name}'>\n{content}\n</note>" f"<note name='{name}'>\n{content}\n</note>"
for name, content in self._get_notes().items() for name, content in self._get_notes().items()
) + "\n</notes>" )
+ "\n</notes>"
)
style = (arguments or {}).get("style", "simple") style = (arguments or {}).get("style", "simple")
prompt = """ prompt = """
Your task is to provide a summary of the notes provided below. Your task is to provide a summary of the notes provided below.