Standalone MCP server that wraps deterministic parts of the external l2p package for human-in-the-loop planning workflows.
This repo assumes the MCP client already has model access. The model only needs to decide whether a change belongs to the domain or the task, then format the update using the tool docstring. After that, the server takes over: it parses the update, merges it into the current planning state, infers requirements when needed, and generates updated PDDL artifacts.
server.pyDefines the MCP tools, configures the HTTP transport defaults, and starts the serverserver_helpers.pyContains the parsing, merge, and normalization logic used by the MCP toolsDockerfileBuilds the Docker image for running the MCP server as a local servicedocker-compose.ymlDefines the local Docker Compose service that exposes the MCP server onlocalhost:8002.dockerignoreKeeps the Docker build context small by excluding caches and local development files
test_offline.pyDirect unit tests for parsing, merging, generation, and error handlingtest_mcp_offline.pyEnd-to-end MCP tests that exercise the server over a temporarystdioclient using the active Python interpreter
The server exposes two high-level MCP tools:
update_domainupdate_task
update_domain creates or updates domain state from model-written text. It supports:
### TYPES### CONSTANTS### New Predicates### FUNCTIONS- zero, one, or many action updates using:
### Action Parameters### Action Preconditions### Action Effects- optional
## NEXT ACTIONseparators
The tool can:
- start from an empty domain when no prior
domainis provided - merge into an existing domain when
domainis provided - infer
:requirements - generate final domain PDDL when
domain_nameis provided
For multi-action updates, action names can come from:
- text placed before each action block
action_namepassed as a list
update_task creates or updates task/problem state from model-written text. It supports:
### OBJECTS### INITIAL### GOAL
Each section can contain zero, one, or many entries. The tool can:
- start from an empty task when no prior
taskis provided - merge into an existing task when
taskis provided - replace fields like
goalwithreplace_fields - generate final problem PDDL when
domain_nameandproblem_nameare provided
Task generation requires the merged task to contain:
objectsinitialgoal
- The user asks for a planning change
- The client-side model chooses
update_domainorupdate_task - The model writes the update using the format documented in the selected tool docstring
- The MCP server parses the update, merges it into the current structured state, and returns updated PDDL when enough information is present
This keeps the server stateless while still supporting incremental edits. A client can update only the goal, one action, several actions, a few predicates, or any other partial fragment without restarting the whole formulation process.
Run the direct and MCP integration tests from the root directory with:
python3 -m unittest -v tests.test_offline tests.test_mcp_offlineThe current test suite covers:
- domain creation and updates
- task creation and updates
- multi-action parsing and merging
- empty action sections
- type hierarchy parsing
- file-based update input
- MCP tool calls
- domain and task error paths
This repo supports running the MCP server as a long-lived local Docker service over streamable HTTP. The Docker files live in the server/ directory.
The container is configured to:
- use
streamable-http - listen on
0.0.0.0:8002inside the container - expose the MCP endpoint at
/mcp - restart automatically with
unless-stopped
Run the Dockerized server from the repo root with:
docker compose -f server/docker-compose.yml up -d --buildThat starts the container in the background. Once it is running, the MCP endpoint is available at:
http://localhost:8002/mcp
Helpful commands:
docker compose -f server/docker-compose.yml logs -fdocker compose -f server/docker-compose.yml psdocker compose -f server/docker-compose.yml restartdocker compose -f server/docker-compose.yml down
For the container to come back after a reboot, make sure Docker Desktop is configured to start automatically when you log in.
- An interpreter where
l2pis installed mcppython3in the current environment
python3 server/server.pyBy default, that now starts the server with streamable-http on:
http://127.0.0.1:8002/mcp
To override the host port or path:
MCP_TRANSPORT=streamable-http MCP_HOST=127.0.0.1 MCP_PORT=8002 python3 server/server.pyTo force the older stdio transport for a client that wants to spawn the process directly:
MCP_TRANSPORT=stdio python3 server/server.py{
"mcpServers": {
"l2p": {
"command": "python3",
"args": ["/absolute/path/to/server/server.py"],
"env": {
"MCP_TRANSPORT": "stdio"
}
}
}
}If your MCP client supports HTTP transports, point it at http://localhost:8002/mcp when the Docker container is running, or http://127.0.0.1:8002/mcp when you run the server directly.