From d683faf6f33de028c51e2a284974cbbdeaf61fda Mon Sep 17 00:00:00 2001 From: William Fawcett Date: Tue, 28 Apr 2026 11:10:37 +0100 Subject: [PATCH 1/3] chore: remove EVEMCPClient and its placeholder test Per collaborator guidance for v0.0.1, the in-tree MCP client demo is not the right way to consume an MCP server: it spawns the server itself as a subprocess and shipping it from the package's public API mis-signals that pattern as endorsed. Real hosts (Claude Desktop, Cursor, Claude Code, etc.) launch the server themselves via their own config. Also trim config.py to just SERVER_MODULE; PYTHON_BIN / PYTHON_PATH existed only to feed the removed StdioServerParameters call. Tests still import SERVER_MODULE. Can be reintroduced later under examples/ alongside correct usage. --- src/eve_mcp/__init__.py | 6 +-- src/eve_mcp/client.py | 109 ---------------------------------------- src/eve_mcp/config.py | 14 +----- tests/test_client.py | 5 -- 4 files changed, 2 insertions(+), 132 deletions(-) delete mode 100644 src/eve_mcp/client.py delete mode 100644 tests/test_client.py diff --git a/src/eve_mcp/__init__.py b/src/eve_mcp/__init__.py index e8ca9fc..3f0f05a 100644 --- a/src/eve_mcp/__init__.py +++ b/src/eve_mcp/__init__.py @@ -1,5 +1 @@ -"""EVE MCP server and client.""" - -from .client import EVEMCPClient - -__all__ = ["EVEMCPClient"] +"""EVE MCP server.""" diff --git a/src/eve_mcp/client.py b/src/eve_mcp/client.py deleted file mode 100644 index c9158b5..0000000 --- a/src/eve_mcp/client.py +++ /dev/null @@ -1,109 +0,0 @@ -"""MCP client for the EVE server (server via stdio).""" - -from __future__ import annotations - -import json -import os -from contextlib import AsyncExitStack -from typing import Any - -from mcp import ( # pylint: disable=wrong-import-order - ClientSession, - StdioServerParameters, -) -from mcp.client.stdio import stdio_client # pylint: disable=wrong-import-order - -from .config import PYTHON_BIN, PYTHON_PATH, SERVER_MODULE - - -class EVEToolError(RuntimeError): - """Raised when the MCP server returns an error for a tool call.""" - - -class EVEMCPClient: - """Async context manager wrapping an MCP stdio session. - - Usage:: - - async with EVEMCPClient() as eve: - health = await eve.check_health() - answer = await eve.query("What is Earth Observation?") - """ - - def __init__(self) -> None: - self._session: ClientSession | None = None - self._stack: AsyncExitStack | None = None - - async def __aenter__(self) -> EVEMCPClient: - params = StdioServerParameters( - command=PYTHON_BIN, - args=["-m", SERVER_MODULE], - env={**os.environ, "PYTHONPATH": PYTHON_PATH}, - ) - stack = AsyncExitStack() - self._stack = await stack.__aenter__() - read, write = await stack.enter_async_context(stdio_client(params)) - session = ClientSession(read, write) - self._session = await stack.enter_async_context(session) - await self._session.initialize() - return self - - async def __aexit__( - self, - exc_type: type[BaseException] | None, - exc_val: BaseException | None, - exc_tb: Any, - ) -> None: - if self._stack is not None: - await self._stack.__aexit__(exc_type, exc_val, exc_tb) - self._stack = None - self._session = None - - @staticmethod - def _parse_result(result: Any) -> Any: - """Extract JSON from a tool result, raising on server errors.""" - text = result.content[0].text - if result.isError: - raise EVEToolError(text) - return json.loads(text) - - async def query( - self, - question: str, - collections: str = "eve-public", - k: int = 5, - ) -> dict: - """Call ``query_eve`` on the MCP server.""" - assert self._session is not None, "Use as async context manager" - result = await self._session.call_tool( - "query_eve", - { - "question": question, - "collections": collections, - "k": k, - }, - ) - return self._parse_result(result) - - async def extract_factuality_issues( - self, question: str, python_code: str - ) -> str: - """Call ``extract_factuality_issues`` on the MCP server.""" - assert self._session is not None, "Use as async context manager" - result = await self._session.call_tool( - "extract_factuality_issues", - {"question": question, "python_code": python_code}, - ) - return self._parse_result(result) - - async def list_collections(self) -> list: - """Call ``list_eve_collections`` on the MCP server.""" - assert self._session is not None, "Use as async context manager" - result = await self._session.call_tool("list_eve_collections", {}) - return self._parse_result(result) - - async def check_health(self) -> dict: - """Call ``check_eve_health`` on the MCP server.""" - assert self._session is not None, "Use as async context manager" - result = await self._session.call_tool("check_eve_health", {}) - return self._parse_result(result) diff --git a/src/eve_mcp/config.py b/src/eve_mcp/config.py index a7f4814..b14f456 100644 --- a/src/eve_mcp/config.py +++ b/src/eve_mcp/config.py @@ -1,15 +1,3 @@ -"""This file contains module constants.""" +"""Module-level configuration constants.""" -import os -import sys -from pathlib import Path - -PYTHON_BIN = os.getenv( - "EVE_MCP_PYTHON", - sys.executable, -) SERVER_MODULE = "eve_mcp.server" -PYTHON_PATH = str(Path(__file__).resolve().parent.parent) -PYTHON_PATH += ":" + os.path.join( - str(Path(__file__).parent.parent.parent), "lib", "eve-api", "src" -) diff --git a/tests/test_client.py b/tests/test_client.py deleted file mode 100644 index 2e11d9e..0000000 --- a/tests/test_client.py +++ /dev/null @@ -1,5 +0,0 @@ -"""This file tests the client module.""" - - -def test_pass(): - """A dummy test to automatically pass, so CI/CD can be built incrementally.""" From bdf0633d59edf54473b83fb8544d0116bda514de Mon Sep 17 00:00:00 2001 From: William Fawcett Date: Tue, 28 Apr 2026 11:10:52 +0100 Subject: [PATCH 2/3] docs(readme): expand with install, config, run, and dev sections Replace the one-line stub with: alpha/WIP banner, the list of MCP tools the server registers, install instructions (including submodule init), env var table for EVE_EMAIL / EVE_PASSWORD / EVE_BASE_URL, the stdio run command, and pre-commit setup. Addresses the v0.0.1 README ask from the collaborator's release checklist. --- README.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 75 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0994bc2..1163f9c 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,78 @@ # EVE MCP -An MCP server for EVE. +An [MCP](https://modelcontextprotocol.io/) server that exposes the +[EVE](https://eve-chat.chat) Earth Observation chat API as a set of MCP +tools. +> **Status: alpha (v0.0.1) — work in progress.** APIs, tool names, and +> wire formats may change without notice. Not yet recommended for +> production use. + +## Tools + +The server registers the following MCP tools: + +- `query_eve` — query the EVE RAG system and return the assembled answer +- `list_eve_collections` — list publicly available document collections +- `check_eve_health` — unauthenticated health check against the EVE API +- `extract_factuality_issues` — analyze a Google Earth Engine Python + script and surface scientific assumptions worth verifying +- `assess_factuality_issue` — produce an expert-style assessment of an + issue raised against a GEE script + +## Installation + +The project uses [Poetry](https://python-poetry.org/) and a Git +submodule for the upstream `eve-api` client. + +```bash +git clone --recurse-submodules https://github.com//eve-mcp.git +cd eve-mcp +poetry install +``` + +If you cloned without `--recurse-submodules`: + +```bash +git submodule update --init --recursive +``` + +Supported Python versions: 3.11–3.14. + +## Configuration + +The server authenticates to EVE on first tool invocation using the +following environment variables: + +| Variable | Required | Default | Purpose | +| --------------- | -------- | ----------------------------- | -------------------------------- | +| `EVE_EMAIL` | yes | — | EVE account email | +| `EVE_PASSWORD` | yes | — | EVE account password | +| `EVE_BASE_URL` | no | `https://api.eve-chat.chat` | EVE API base URL | + +## Running the server + +Over stdio (the standard MCP transport): + +```bash +poetry run python -m eve_mcp.server +``` + +## Development + +Install the pre-commit hooks once: + +```bash +poetry run pre-commit install +``` + +Run the full hook suite (black, isort, mypy, pylint, pytest+coverage) +against all files: + +```bash +poetry run pre-commit run --all-files +``` + +## License + +MIT — see [LICENSE](LICENSE). From ddddb1a8cc8386301dd9aa6c73d1dc7fee2b2acf Mon Sep 17 00:00:00 2001 From: William Fawcett Date: Tue, 28 Apr 2026 11:24:44 +0100 Subject: [PATCH 3/3] docs(readme): add copyright notice Attribute copyright to Trillium Technologies Ltd, 2026. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 1163f9c..3dd6afe 100644 --- a/README.md +++ b/README.md @@ -76,3 +76,7 @@ poetry run pre-commit run --all-files ## License MIT — see [LICENSE](LICENSE). + +## Copyright + +© 2026 Trillium Technologies Ltd.