Skip to content

Add FarmVibes MCP server - #256

Draft
Renato L. de F. Cunha (renatolfc) wants to merge 16 commits into
microsoft:mainfrom
renatolfc:feature/farmvibes-mcp
Draft

Add FarmVibes MCP server#256
Renato L. de F. Cunha (renatolfc) wants to merge 16 commits into
microsoft:mainfrom
renatolfc:feature/farmvibes-mcp

Conversation

@renatolfc

@renatolfc Renato L. de F. Cunha (renatolfc) commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

FarmVibes can currently be automated through Python or REST, but MCP clients cannot reuse that behavior directly because vibe_core was pinned to Pydantic 1 while the current MCP SDK requires Pydantic 2.

This PR makes vibe_core compatible with Pydantic 1.10.17 through 2.x by running the existing models through pydantic.v1. Legacy service, worker, and notebook environments deliberately remain on Pydantic 1, so adding MCP does not force unrelated geospatial and ML dependency upgrades. The standalone MCP environment selects Pydantic 2. This also leaves a clear upgrade path: each runtime can modernize independently, and native Pydantic 2 models can come later without blocking MCP now. fastapi-utils enum usage moves to the already-installed strenum package.

The new standalone farmvibes-mcp package requires Python 3.11, runs over stdio, and imports FarmvibesAiClient directly. Local/remote URL discovery, bearer-token handling, request payloads, output rewriting, and error behavior therefore remain in one place. It exposes seven tools: list/describe workflows, submit runs, list/get runs, retrieve output, and cancel runs.

The MCP process has its own environment because MCP 2 and the legacy FastAPI service stack require incompatible Starlette, Uvicorn, and OpenTelemetry versions. CI builds all six packages through PEP 517 and checks those dependency sets separately.

Validation

  • 514 existing vibe_core, vibe_common, vibe_server, and vibe_agent tests pass under both Pydantic 1.10.26 and Pydantic 2.13.4.
  • Four MCP tests pass, including a real subprocess stdio session backed by the actual client against a local HTTP service.
  • All six wheels build; pip check passes in separate service-stack and MCP environments; Ruff and pinned Pyright pass.
  • The compatibility range resolves Pydantic 2.10.6 on Python 3.8, the legacy container requirements resolve Pydantic 1.10.26, and MCP resolves Pydantic 2.13.4 with MCP 2.1.1.
  • Both container requirement sets resolve on Python 3.11, and the worker's existing Planetary Computer/morecantile stack imports unchanged under Pydantic 1.
  • A 32-core Codespace built a real two-node local k3d cluster. MCP discovered 90 workflows, described helloworld, submitted run 2fd63c03-6a97-4050-a084-1227aa0e3cac, observed queued → done, and retrieved the raster output.
  • Codespaces validation also fixed host cgroup delegation in the devcontainer and removed unnecessary Docker TTY flags that made noninteractive setup hang. The test cluster was deleted afterward.

The user guide includes isolated installation, VS Code and generic MCP configuration, tool usage, local/remote discovery, a tested Codespaces setup, and cleanup.

This first version is stdio-only. It intentionally skips prompts, resources, asset downloads, arbitrary existing-asset submission, and a blocking wait tool.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a standalone stdio MCP server while migrating FarmVibes to Python 3.11 and Pydantic 2’s v1 compatibility layer.

Changes:

  • Adds seven MCP tools with unit and subprocess tests.
  • Migrates enums and Pydantic imports while preserving wire formats.
  • Updates package metadata, CI, environments, and documentation for Python 3.11.

Reviewed changes

Copilot reviewed 56 out of 56 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/vibe_server/vibe_server/workflow/spec_validator.py Updates node-type validation.
src/vibe_server/vibe_server/workflow/spec_parser.py Migrates task enums and parsing.
src/vibe_server/vibe_server/workflow/runner/runner.py Migrates workflow event enum.
src/vibe_server/vibe_server/workflow/parameter.py Corrects schema import.
src/vibe_server/vibe_server/server.py Uses v1 compatibility and stdlib enums.
src/vibe_server/tests/test_remote_workflow_runner.py Updates compatibility imports.
src/vibe_server/tests/test_op_parallelism.py Patches the v1 namespace.
src/vibe_server/setup.py Targets Python 3.11.
src/vibe_notebook/setup.py Declares Python 3.11 support.
src/vibe_lib/vibe_lib/geometry.py Migrates geometry enum.
src/vibe_lib/vibe_lib/comet_farm/comet_server.py Uses Pydantic v1 model.
src/vibe_lib/vibe_lib/comet_farm/comet_model.py Uses Pydantic v1 models.
src/vibe_lib/vibe_lib/airbus.py Migrates Airbus enums.
src/vibe_lib/setup.py Targets Python 3.11.
src/vibe_dev/vibe_dev/mock_utils.py Uses Pydantic v1 model.
src/vibe_dev/setup.py Targets Python 3.11.
src/vibe_core/vibe_core/datamodel.py Migrates models and run statuses.
src/vibe_core/vibe_core/data/utils.py Uses Pydantic v1 model.
src/vibe_core/vibe_core/data/sentinel.py Migrates processing-level enum.
src/vibe_core/vibe_core/data/json_converter.py Preserves v1 serialization.
src/vibe_core/vibe_core/data/core_types.py Preserves v1 core models.
src/vibe_core/vibe_core/client.py Migrates cluster enum.
src/vibe_core/vibe_core/cli/osartifacts.py Uses importlib.resources.
src/vibe_core/pyproject.toml Requires Python 3.11 and Pydantic 2.
src/vibe_common/vibe_common/schemas.py Preserves v1 dataclasses.
src/vibe_common/vibe_common/messaging.py Migrates models and enums.
src/vibe_common/vibe_common/dropdapr.py Uses v1 configuration.
src/vibe_common/tests/test_messaging.py Updates serialization imports.
src/vibe_common/setup.py Updates runtime requirements.
src/vibe_agent/setup.py Declares Python 3.11 support.
src/farmvibes_mcp/tests/test_stdio.py Tests a real stdio session.
src/farmvibes_mcp/tests/test_server.py Tests all MCP tools.
src/farmvibes_mcp/pyproject.toml Defines the MCP package.
src/farmvibes_mcp/farmvibes_mcp/server.py Implements seven MCP tools.
src/farmvibes_mcp/farmvibes_mcp/__main__.py Adds module execution.
src/farmvibes_mcp/farmvibes_mcp/__init__.py Exports the MCP server.
resources/envs/worker-requirements.txt Updates worker dependencies.
resources/envs/services-requirements.txt Updates service dependencies.
ops/weed_detection/weed_detection.py Migrates operation enum.
ops/merge_geometries/merge_geometries.py Migrates merge enum.
notebooks/weed_detection/weed_detection_env.yaml Targets Python 3.11.
notebooks/sensor/optimal_locations.yaml Targets Python 3.11.
notebooks/segment_anything/env_gpu.yaml Updates Python, but conflicts with builds.
notebooks/segment_anything/env_cpu.yaml Targets Python 3.11.
notebooks/irrigation/env.yaml Targets Python 3.11.
notebooks/heatmaps/nutrients.yaml Targets Python 3.11.
notebooks/env.yaml Updates the shared environment.
notebooks/deepmc/deepmc_env.yaml Updates Python, but conflicts with PyTorch.
notebooks/deepmc_neighbors/deepmc_neighbors_env.yaml Updates Python and Pydantic.
notebooks/crop_segmentation/crop_env.yaml Updates Python, but conflicts with PyTorch.
notebooks/crop_cycles/env.yaml Updates Python, but conflicts with TensorFlow.
docs/source/index.md Links MCP documentation.
docs/source/docfiles/markdown/QUICKSTART.md Updates Python prerequisite.
docs/source/docfiles/markdown/MCP.md Documents MCP installation and tools.
docs/source/docfiles/markdown/AKS.md Updates Python prerequisite.
.github/workflows/lint-test.yml Builds and tests the MCP package.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread notebooks/deepmc/deepmc_env.yaml Outdated
Comment thread notebooks/segment_anything/env_gpu.yaml Outdated
Comment thread notebooks/crop_segmentation/crop_env.yaml Outdated
Comment thread notebooks/crop_cycles/env.yaml Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 38 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 38 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated no new comments.

Suppressed comments (2)

Previously missed (2) — in code that hasn't changed since the last review.

src/farmvibes_mcp/farmvibes_mcp/server.py:75

  • Malformed GeoJSON is validated only after shape() succeeds. Inputs permitted by the dict schema such as {} raise an AttributeError in supported Shapely versions, bypassing _execute's handled exceptions and exposing an internal parsing error instead of the documented geometry validation message. Normalize parsing failures before checking is_empty/is_valid (and cover malformed GeoJSON in the tests).
        parsed_geometry = shape(geometry)
        if parsed_geometry.is_empty or not parsed_geometry.is_valid:
            raise ValueError("geometry must be a nonempty valid GeoJSON geometry")

src/farmvibes_mcp/farmvibes_mcp/server.py:114

  • get_run still downloads the full /v0/runs/{id} response and _jsonable(run) serializes the potentially large output before it is removed. Large run outputs therefore impose the same REST transfer and substantial peak memory cost this tool is intended to avoid, and serialization can fail on data that would never be returned. Fetch only the required run fields (or add a client/API option that excludes output) instead of discarding it afterward.
    run = _execute(lambda: _client().describe_run(run_id))
    result = _jsonable(run)
    result.pop("output", None)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated 1 comment.

Comment thread src/farmvibes_mcp/farmvibes_mcp/server.py Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated 2 comments.

Comment thread .github/workflows/lint-test.yml Outdated
Comment thread src/vibe_core/pyproject.toml

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated no new comments.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 40 out of 40 changed files in this pull request and generated 1 comment.

Comment on lines +116 to +119
run = _execute(lambda: _client().describe_run(run_id))
result = _jsonable(run)
result.pop("output", None)
return result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants