From 6843261153ddf68d748f4e6cfbe437c637556505 Mon Sep 17 00:00:00 2001 From: Jungkoo Kang Date: Fri, 10 Oct 2025 10:38:50 -0400 Subject: [PATCH 1/7] Create workflow for MCP Server --- .github/workflows/main.yaml | 21 ++++++++++ mcp/requirements_test.txt | 2 + mcp/tests/__init__.py | 0 mcp/tests/data/pddl/pddl_example.py | 56 ++++++++++++++++++++++++++ mcp/tests/helpers/__init__.py | 0 mcp/tests/helpers/mcp_client_helper.py | 7 ++++ mcp/tests/test_container.py | 23 +++++++++++ 7 files changed, 109 insertions(+) create mode 100644 mcp/requirements_test.txt create mode 100644 mcp/tests/__init__.py create mode 100644 mcp/tests/data/pddl/pddl_example.py create mode 100644 mcp/tests/helpers/__init__.py create mode 100644 mcp/tests/helpers/mcp_client_helper.py create mode 100644 mcp/tests/test_container.py diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 8d2d20b..f0fee4c 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -6,6 +6,7 @@ on: - "*.*.*" branches: - actions* + jobs: build_linux: name: Build wheels on ubuntu-latest ${{ matrix.arch }} ${{ matrix.python }} @@ -76,3 +77,23 @@ jobs: with: files: | ./wheelhouse/** + build_mcp_server: + name: Build and Test MCP Server Container + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Set up Docker + uses: docker/setup-docker-action@v4 + - name: Build MCP Server Container + run: docker build -f mcp/Dockerfile.mcp -t kstar-planner:v0.0 . + - name: Run MCP Server Container + run: docker run -p 8000:8000 --name kstar-planner -d kstar-planner:v0.0 + - name: Install Test dependencies + run: | + pip install -r mcp/requirements.txt + pip install -r mcp/requirements_test.txt + - name: Test MCP Server Container + env: + MCP_SERVER_URL: http://localhost:8000/mcp + run: pytest mcp/tests/test_container.py diff --git a/mcp/requirements_test.txt b/mcp/requirements_test.txt new file mode 100644 index 0000000..0bcf21c --- /dev/null +++ b/mcp/requirements_test.txt @@ -0,0 +1,2 @@ +pytest +pytest-asyncio \ No newline at end of file diff --git a/mcp/tests/__init__.py b/mcp/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mcp/tests/data/pddl/pddl_example.py b/mcp/tests/data/pddl/pddl_example.py new file mode 100644 index 0000000..1252640 --- /dev/null +++ b/mcp/tests/data/pddl/pddl_example.py @@ -0,0 +1,56 @@ +domain = """ +(define (domain blocksworld) + (:requirements :strips :typing) + (:types block) + + (:predicates + (on ?x ?y - block) ; Block ?x is on top of block ?y + (ontable ?x - block) ; Block ?x is on the table + (clear ?x - block) ; Nothing is on top of block ?x + (handempty) ; The robot arm is empty + (holding ?x - block) ; The robot arm is holding block ?x + ) + + (:action pick-up + :parameters (?x - block) + :precondition (and (clear ?x) (ontable ?x) (handempty)) + :effect (and (not (ontable ?x)) (not (clear ?x)) (not (handempty)) (holding ?x)) + ) + + (:action put-down + :parameters (?x - block) + :precondition (holding ?x) + :effect (and (not (holding ?x)) (clear ?x) (handempty) (ontable ?x)) + ) + + (:action stack + :parameters (?x ?y - block) + :precondition (and (holding ?x) (clear ?y)) + :effect (and (not (holding ?x)) (not (clear ?y)) (clear ?x) (handempty) (on ?x ?y)) + ) + + (:action unstack + :parameters (?x ?y - block) + :precondition (and (on ?x ?y) (clear ?x) (handempty)) + :effect (and (not (on ?x ?y)) (not (clear ?x)) (clear ?y) (holding ?x) (not (handempty))) + ) +) +""" + +problem = """ +(define (problem p01) + (:domain blocksworld) + (:objects A B - block) ; Two blocks, A and B + (:init + (ontable A) ; Block A is on the table + (on B A) ; Block B is on top of block A + (clear B) ; Nothing is on top of B + (handempty) ; The arm is empty + ) + (:goal (and + (on A B) ; Block A should be on block B + (clear A) ; Block A should be clear + (ontable B) ; Block B should be on the table + )) +) +""" diff --git a/mcp/tests/helpers/__init__.py b/mcp/tests/helpers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mcp/tests/helpers/mcp_client_helper.py b/mcp/tests/helpers/mcp_client_helper.py new file mode 100644 index 0000000..d37ebc0 --- /dev/null +++ b/mcp/tests/helpers/mcp_client_helper.py @@ -0,0 +1,7 @@ +from fastmcp import Client +from fastmcp.client.transports import StreamableHttpTransport + + +def get_client(server_url) -> Client: + transport = StreamableHttpTransport(url=server_url) + return Client(transport) diff --git a/mcp/tests/test_container.py b/mcp/tests/test_container.py new file mode 100644 index 0000000..b5d0c4e --- /dev/null +++ b/mcp/tests/test_container.py @@ -0,0 +1,23 @@ +import os +import pytest +from mcp.tests.helpers.mcp_client_helper import get_client +from mcp.tests.data.pddl.pddl_example import domain, problem + + +class TestMcpContainer: + @pytest.mark.skipif( + "MCP_SERVER_URL" not in os.environ, reason="Requires MCP_SERVER_URL to be set" + ) + @pytest.mark.asyncio + async def test_planner_tool_t(self) -> None: + client = get_client(os.getenv("MCP_SERVER_URL", "http://localhost:8000/mcp")) + + async with client: + tool_list = await client.list_tools() + assert tool_list is not None + + payload = await client.call_tool( + "KstarPlannerUnorderedTopQ", + {"domain": domain, "problem": problem}, + ) + assert payload is not None From 5e5f0aba0c2e1e17311c16b6dbf0c68ffed77cc6 Mon Sep 17 00:00:00 2001 From: Jungkoo Kang Date: Fri, 10 Oct 2025 10:42:29 -0400 Subject: [PATCH 2/7] Add test dependencies for MCP server --- mcp/requirements_test.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mcp/requirements_test.txt b/mcp/requirements_test.txt index 0bcf21c..9d83c8d 100644 --- a/mcp/requirements_test.txt +++ b/mcp/requirements_test.txt @@ -1,2 +1,3 @@ pytest -pytest-asyncio \ No newline at end of file +pytest-asyncio +mcp \ No newline at end of file From 1e137d83c5355085282f9a5ba6399c23de699d77 Mon Sep 17 00:00:00 2001 From: Jungkoo Kang Date: Fri, 10 Oct 2025 10:48:42 -0400 Subject: [PATCH 3/7] Use python 3.11 for testing mcp container --- .github/workflows/main.yaml | 10 +++++++++- mcp/requirements_test.txt | 3 +-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index f0fee4c..8277045 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -80,9 +80,17 @@ jobs: build_mcp_server: name: Build and Test MCP Server Container runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.11"] steps: - name: Checkout uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 # Use the latest version of setup-python + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' # Option - name: Set up Docker uses: docker/setup-docker-action@v4 - name: Build MCP Server Container @@ -96,4 +104,4 @@ jobs: - name: Test MCP Server Container env: MCP_SERVER_URL: http://localhost:8000/mcp - run: pytest mcp/tests/test_container.py + run: pytest mcp/tests/test_container.py \ No newline at end of file diff --git a/mcp/requirements_test.txt b/mcp/requirements_test.txt index 9d83c8d..0bcf21c 100644 --- a/mcp/requirements_test.txt +++ b/mcp/requirements_test.txt @@ -1,3 +1,2 @@ pytest -pytest-asyncio -mcp \ No newline at end of file +pytest-asyncio \ No newline at end of file From a8ceff11c8ee8f282dd11c6cb7b949ac944a9ca8 Mon Sep 17 00:00:00 2001 From: Jungkoo Kang Date: Fri, 10 Oct 2025 10:51:57 -0400 Subject: [PATCH 4/7] Update MCP workflow --- .github/workflows/main.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 8277045..48545d3 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -104,4 +104,4 @@ jobs: - name: Test MCP Server Container env: MCP_SERVER_URL: http://localhost:8000/mcp - run: pytest mcp/tests/test_container.py \ No newline at end of file + run: python -m pytest mcp/tests/test_container.py \ No newline at end of file From 2a7d3d8846a22f7f37ec2fe3026f61b258c3a05b Mon Sep 17 00:00:00 2001 From: Jungkoo Kang Date: Fri, 10 Oct 2025 11:48:15 -0400 Subject: [PATCH 5/7] Resolve Package name conflict for mcp --- .github/workflows/main.yaml | 4 ++-- mcp/Dockerfile.mcp | 9 --------- mcp/requirements_test.txt | 2 -- mcp/tests/helpers/mcp_client_helper.py | 7 ------- mcp_server/.vscode/settings.json | 7 +++++++ mcp_server/Dockerfile.mcp | 9 +++++++++ {mcp => mcp_server}/README.md | 4 ++-- {mcp => mcp_server}/__init__.py | 0 {mcp => mcp_server}/data/__init__.py | 0 {mcp => mcp_server}/data/descriptions/__init__.py | 0 .../data/descriptions/tool_descriptions.py | 0 {mcp => mcp_server}/data_models/__init__.py | 0 {mcp => mcp_server}/data_models/tool_data_models.py | 0 {mcp => mcp_server}/helpers/__init__.py | 0 {mcp => mcp_server}/helpers/file_helper.py | 0 {mcp => mcp_server}/helpers/planner_helper.py | 0 {mcp => mcp_server}/requirements.txt | 0 mcp_server/requirements_test.txt | 3 +++ {mcp => mcp_server}/server.py | 0 {mcp => mcp_server}/tests/__init__.py | 0 {mcp/tests/helpers => mcp_server/tests/data}/__init__.py | 0 mcp_server/tests/data/pddl/__init__.py | 0 {mcp => mcp_server}/tests/data/pddl/pddl_example.py | 0 {mcp => mcp_server}/tests/test_container.py | 9 +++++++-- 24 files changed, 30 insertions(+), 24 deletions(-) delete mode 100644 mcp/Dockerfile.mcp delete mode 100644 mcp/requirements_test.txt delete mode 100644 mcp/tests/helpers/mcp_client_helper.py create mode 100644 mcp_server/.vscode/settings.json create mode 100644 mcp_server/Dockerfile.mcp rename {mcp => mcp_server}/README.md (90%) rename {mcp => mcp_server}/__init__.py (100%) rename {mcp => mcp_server}/data/__init__.py (100%) rename {mcp => mcp_server}/data/descriptions/__init__.py (100%) rename {mcp => mcp_server}/data/descriptions/tool_descriptions.py (100%) rename {mcp => mcp_server}/data_models/__init__.py (100%) rename {mcp => mcp_server}/data_models/tool_data_models.py (100%) rename {mcp => mcp_server}/helpers/__init__.py (100%) rename {mcp => mcp_server}/helpers/file_helper.py (100%) rename {mcp => mcp_server}/helpers/planner_helper.py (100%) rename {mcp => mcp_server}/requirements.txt (100%) create mode 100644 mcp_server/requirements_test.txt rename {mcp => mcp_server}/server.py (100%) rename {mcp => mcp_server}/tests/__init__.py (100%) rename {mcp/tests/helpers => mcp_server/tests/data}/__init__.py (100%) create mode 100644 mcp_server/tests/data/pddl/__init__.py rename {mcp => mcp_server}/tests/data/pddl/pddl_example.py (100%) rename {mcp => mcp_server}/tests/test_container.py (69%) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 48545d3..886bd68 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -94,7 +94,7 @@ jobs: - name: Set up Docker uses: docker/setup-docker-action@v4 - name: Build MCP Server Container - run: docker build -f mcp/Dockerfile.mcp -t kstar-planner:v0.0 . + run: docker build -f mcp_server/Dockerfile.mcp -t kstar-planner:v0.0 . - name: Run MCP Server Container run: docker run -p 8000:8000 --name kstar-planner -d kstar-planner:v0.0 - name: Install Test dependencies @@ -104,4 +104,4 @@ jobs: - name: Test MCP Server Container env: MCP_SERVER_URL: http://localhost:8000/mcp - run: python -m pytest mcp/tests/test_container.py \ No newline at end of file + run: python -m pytest mcp_server/tests/test_container.py \ No newline at end of file diff --git a/mcp/Dockerfile.mcp b/mcp/Dockerfile.mcp deleted file mode 100644 index 53f102f..0000000 --- a/mcp/Dockerfile.mcp +++ /dev/null @@ -1,9 +0,0 @@ -FROM python:3.11 - -RUN apt-get update && apt-get install -y cmake make g++ -COPY mcp /src/mcp -WORKDIR /src -RUN pip install -r mcp/requirements.txt - -EXPOSE 8000 -CMD ["fastmcp", "run", "mcp/server.py:mcp", "--transport", "http", "--port", "8000", "--host", "0.0.0.0"] \ No newline at end of file diff --git a/mcp/requirements_test.txt b/mcp/requirements_test.txt deleted file mode 100644 index 0bcf21c..0000000 --- a/mcp/requirements_test.txt +++ /dev/null @@ -1,2 +0,0 @@ -pytest -pytest-asyncio \ No newline at end of file diff --git a/mcp/tests/helpers/mcp_client_helper.py b/mcp/tests/helpers/mcp_client_helper.py deleted file mode 100644 index d37ebc0..0000000 --- a/mcp/tests/helpers/mcp_client_helper.py +++ /dev/null @@ -1,7 +0,0 @@ -from fastmcp import Client -from fastmcp.client.transports import StreamableHttpTransport - - -def get_client(server_url) -> Client: - transport = StreamableHttpTransport(url=server_url) - return Client(transport) diff --git a/mcp_server/.vscode/settings.json b/mcp_server/.vscode/settings.json new file mode 100644 index 0000000..9b38853 --- /dev/null +++ b/mcp_server/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true +} \ No newline at end of file diff --git a/mcp_server/Dockerfile.mcp b/mcp_server/Dockerfile.mcp new file mode 100644 index 0000000..7c578e6 --- /dev/null +++ b/mcp_server/Dockerfile.mcp @@ -0,0 +1,9 @@ +FROM python:3.11 + +RUN apt-get update && apt-get install -y cmake make g++ +COPY mcp_server /src/mcp_server +WORKDIR /src +RUN pip install -r mcp_server/requirements.txt + +EXPOSE 8000 +CMD ["fastmcp", "run", "mcp_server/server.py:mcp", "--transport", "http", "--port", "8000", "--host", "0.0.0.0"] \ No newline at end of file diff --git a/mcp/README.md b/mcp_server/README.md similarity index 90% rename from mcp/README.md rename to mcp_server/README.md index 405e855..e0c6f4c 100644 --- a/mcp/README.md +++ b/mcp_server/README.md @@ -10,10 +10,10 @@ K* MCP Server provides a containerized deployment of Top-K and Top-Q planners fr To build the Docker image, navigate to the project root directory (where the `pyproject.toml` is located) and execute the following command: ```bash -docker build -f mcp/Dockerfile.mcp -t kstar-planner:v0.0 . +docker build -f mcp_server/Dockerfile.mcp -t kstar-planner:v0.0 . ``` -* `-f mcp/Dockerfile.mcp`: Specifies the Dockerfile to use for building the image. +* `-f mcp_server/Dockerfile.mcp`: Specifies the Dockerfile to use for building the image. * `-t kstar-planner:v0.0`: Tags the image as `kstar-planner` with the version `v0.0`. You can replace `v0.0` with your desired version tag. * `.`: Indicates that the build context is the current directory. diff --git a/mcp/__init__.py b/mcp_server/__init__.py similarity index 100% rename from mcp/__init__.py rename to mcp_server/__init__.py diff --git a/mcp/data/__init__.py b/mcp_server/data/__init__.py similarity index 100% rename from mcp/data/__init__.py rename to mcp_server/data/__init__.py diff --git a/mcp/data/descriptions/__init__.py b/mcp_server/data/descriptions/__init__.py similarity index 100% rename from mcp/data/descriptions/__init__.py rename to mcp_server/data/descriptions/__init__.py diff --git a/mcp/data/descriptions/tool_descriptions.py b/mcp_server/data/descriptions/tool_descriptions.py similarity index 100% rename from mcp/data/descriptions/tool_descriptions.py rename to mcp_server/data/descriptions/tool_descriptions.py diff --git a/mcp/data_models/__init__.py b/mcp_server/data_models/__init__.py similarity index 100% rename from mcp/data_models/__init__.py rename to mcp_server/data_models/__init__.py diff --git a/mcp/data_models/tool_data_models.py b/mcp_server/data_models/tool_data_models.py similarity index 100% rename from mcp/data_models/tool_data_models.py rename to mcp_server/data_models/tool_data_models.py diff --git a/mcp/helpers/__init__.py b/mcp_server/helpers/__init__.py similarity index 100% rename from mcp/helpers/__init__.py rename to mcp_server/helpers/__init__.py diff --git a/mcp/helpers/file_helper.py b/mcp_server/helpers/file_helper.py similarity index 100% rename from mcp/helpers/file_helper.py rename to mcp_server/helpers/file_helper.py diff --git a/mcp/helpers/planner_helper.py b/mcp_server/helpers/planner_helper.py similarity index 100% rename from mcp/helpers/planner_helper.py rename to mcp_server/helpers/planner_helper.py diff --git a/mcp/requirements.txt b/mcp_server/requirements.txt similarity index 100% rename from mcp/requirements.txt rename to mcp_server/requirements.txt diff --git a/mcp_server/requirements_test.txt b/mcp_server/requirements_test.txt new file mode 100644 index 0000000..9c556c2 --- /dev/null +++ b/mcp_server/requirements_test.txt @@ -0,0 +1,3 @@ +pytest +pytest-asyncio +mcp[cli] \ No newline at end of file diff --git a/mcp/server.py b/mcp_server/server.py similarity index 100% rename from mcp/server.py rename to mcp_server/server.py diff --git a/mcp/tests/__init__.py b/mcp_server/tests/__init__.py similarity index 100% rename from mcp/tests/__init__.py rename to mcp_server/tests/__init__.py diff --git a/mcp/tests/helpers/__init__.py b/mcp_server/tests/data/__init__.py similarity index 100% rename from mcp/tests/helpers/__init__.py rename to mcp_server/tests/data/__init__.py diff --git a/mcp_server/tests/data/pddl/__init__.py b/mcp_server/tests/data/pddl/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/mcp/tests/data/pddl/pddl_example.py b/mcp_server/tests/data/pddl/pddl_example.py similarity index 100% rename from mcp/tests/data/pddl/pddl_example.py rename to mcp_server/tests/data/pddl/pddl_example.py diff --git a/mcp/tests/test_container.py b/mcp_server/tests/test_container.py similarity index 69% rename from mcp/tests/test_container.py rename to mcp_server/tests/test_container.py index b5d0c4e..43054d7 100644 --- a/mcp/tests/test_container.py +++ b/mcp_server/tests/test_container.py @@ -1,9 +1,14 @@ import os import pytest -from mcp.tests.helpers.mcp_client_helper import get_client -from mcp.tests.data.pddl.pddl_example import domain, problem +from mcp_server.tests.data.pddl.pddl_example import domain, problem +from fastmcp import Client +from fastmcp.client.transports import StreamableHttpTransport +def get_client(server_url) -> Client: + transport = StreamableHttpTransport(url=server_url) + return Client(transport) + class TestMcpContainer: @pytest.mark.skipif( "MCP_SERVER_URL" not in os.environ, reason="Requires MCP_SERVER_URL to be set" From d6046060b4bb21a68dcb8eae24687ec2f97ab098 Mon Sep 17 00:00:00 2001 From: Jungkoo Kang Date: Fri, 10 Oct 2025 11:50:23 -0400 Subject: [PATCH 6/7] Update workflow --- .github/workflows/main.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 886bd68..1161b33 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -99,8 +99,8 @@ jobs: run: docker run -p 8000:8000 --name kstar-planner -d kstar-planner:v0.0 - name: Install Test dependencies run: | - pip install -r mcp/requirements.txt - pip install -r mcp/requirements_test.txt + pip install -r mcp_server/requirements.txt + pip install -r mcp_server/requirements_test.txt - name: Test MCP Server Container env: MCP_SERVER_URL: http://localhost:8000/mcp From a8c682fe732a247e5ff10a9cd100b5c8fecfaeb3 Mon Sep 17 00:00:00 2001 From: Jungkoo Kang Date: Mon, 13 Oct 2025 08:15:42 -0400 Subject: [PATCH 7/7] Add more assertions for a MCP Container test. --- mcp_server/tests/test_container.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mcp_server/tests/test_container.py b/mcp_server/tests/test_container.py index 43054d7..c81b0f0 100644 --- a/mcp_server/tests/test_container.py +++ b/mcp_server/tests/test_container.py @@ -26,3 +26,7 @@ async def test_planner_tool_t(self) -> None: {"domain": domain, "problem": problem}, ) assert payload is not None + assert len(payload.structured_content["plans"]) == 1 + optimal_plan = payload.structured_content["plans"][0] + assert len(optimal_plan["actions"]) == 4 + assert optimal_plan["cost"] == 4