Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions .devcontainer/pi/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# syntax=docker/dockerfile:1.7
#
# Pi devcontainer for agent-client-kernel.
#
# Installs:
# * Node.js 22 (required by pi-acp >= 20 and recommended by pi)
# * pi (the coding agent) from @earendil-works/pi-coding-agent
# * pi-acp (the ACP adapter) — bridges ACP <-> pi --mode rpc
# * pi-mcp-adapter — wires MCP servers (incl. jupyter-mcp-server) through to pi
#
# Pi's default config dir (~/.pi/agent) is populated from the project root
# (.pi/agent/{models,settings,mcp}.json) by COPYing the repo into /home/jovyan,
# matching the pattern used by the Codex devcontainer.

FROM quay.io/jupyter/minimal-notebook:latest

USER root

# Install Node.js 22 (NodeSource), graphviz (used by example notebooks), git, curl.
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates curl git graphviz gnupg \
&& curl -fsSL https://deb.nodesource.com/setup_25.x | bash - \
&& apt-get install -y --no-install-recommends nodejs \
&& rm -rf /var/lib/apt/lists/*

USER jovyan

# Install pi, pi-acp, and the MCP adapter globally as jovyan.
# We use NPM_CONFIG_PREFIX=$HOME/.local so npm doesn't try to write to /usr.
ENV NPM_CONFIG_PREFIX=/home/jovyan/.local
ENV PATH=/home/jovyan/.local/bin:${PATH}

RUN mkdir -p "$HOME/.local" \
&& npm install -g \
@earendil-works/pi-coding-agent \
pi-acp \
pi-mcp-adapter \
&& pi --version \
&& test -x "$HOME/.local/bin/pi-acp"

# Copy project files into jovyan's home (matches the Codex devcontainer layout).
# Crucially, this makes .pi/agent/{models,settings,mcp}.json from the repo land
# at ~/.pi/agent/... so pi picks them up as user-global config.
# Note: --chown does not expand ENV from the base image, so use literal user:group.
COPY --chown=jovyan:users . /home/jovyan/
WORKDIR /home/jovyan

# Install uv and the project (with deps from pyproject.toml) into the
# conda base environment.
SHELL ["conda", "run", "-n", "base", "/bin/bash", "-c"]

# graphviz is used by some example notebooks.
RUN conda install -y -c conda-forge graphviz && conda clean -afy

# `pip install uv` is just bootstrap (the base image has pip but not uv).
# After that we use `uv pip install -e .`, which resolves dependencies
# directly from pyproject.toml.
RUN pip install --upgrade "uv>=0.9.15" \
&& uv pip install --system -e .

# Register pi-mcp-adapter as a pi extension so the adapter loads on startup.
# Best-effort: failure here should not break the image build.
RUN pi install npm:pi-mcp-adapter || true

# Install the kernel for the current user.
RUN python -m agent_client_kernel install --user
73 changes: 73 additions & 0 deletions .devcontainer/pi/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"name": "JupyterLab: Pi ACP",
"build": {
"dockerfile": "Dockerfile",
"context": "../.."
},
"runArgs": [
"--cpus=4",
"--memory=10g"
],
"forwardPorts": [
8888
],
"portsAttributes": {
"8888": {
"label": "JupyterLab",
"requireLocalPort": false,
"onAutoForward": "openBrowser"
}
},
"remoteEnv": {
// Remote LLM provider keys (optional — pi also supports local providers).
"ANTHROPIC_API_KEY": "${localEnv:ANTHROPIC_API_KEY}",
"OPENAI_API_KEY": "${localEnv:OPENAI_API_KEY}",
"GOOGLE_API_KEY": "${localEnv:GOOGLE_API_KEY}",
"DEEPSEEK_API_KEY": "${localEnv:DEEPSEEK_API_KEY}",
// Local LLM provider hosts. Defaults assume the host machine runs LM Studio
// / Ollama on its standard ports; override locally if needed.
"LM_STUDIO_BASE_URL": "${localEnv:LM_STUDIO_BASE_URL}",
"OLLAMA_HOST": "${localEnv:OLLAMA_HOST}"
},
"secrets": {
"ANTHROPIC_API_KEY": {
"description": "Your Anthropic API key (optional — for Claude models in pi).",
"documentationUrl": "https://docs.github.com/en/codespaces/managing-your-codespaces/managing-your-account-specific-secrets-for-github-codespaces"
},
"OPENAI_API_KEY": {
"description": "Your OpenAI API key (optional — for OpenAI models in pi).",
"documentationUrl": "https://docs.github.com/en/codespaces/managing-your-codespaces/managing-your-account-specific-secrets-for-github-codespaces"
},
"GOOGLE_API_KEY": {
"description": "Your Google API key (optional — for Gemini models in pi).",
"documentationUrl": "https://docs.github.com/en/codespaces/managing-your-codespaces/managing-your-account-specific-secrets-for-github-codespaces"
},
"DEEPSEEK_API_KEY": {
"description": "Your DeepSeek API key (optional).",
"documentationUrl": "https://docs.github.com/en/codespaces/managing-your-codespaces/managing-your-account-specific-secrets-for-github-codespaces"
}
},
"containerEnv": {
"JUPYTER_PORT": "8888",
"GRANT_SUDO": "yes",
"ACP_AGENT_COMMAND": "/home/jovyan/.local/bin/pi-acp",
"ACP_AGENT_ARGS": "",
"RESTARTABLE": "false",
// pi-acp option: let ACP clients send embedded resource blocks. The
// kernel currently doesn't, but enabling this is harmless and matches
// the recommended pi-acp setup.
"PI_ACP_ENABLE_EMBEDDED_CONTEXT": "true",
// pi-mcp-adapter (auto-loaded as a pi extension) opens browser tabs on
// the host via $BROWSER / xdg-open for its MCP-UI sessions and OAuth
// helpers. In a Codespaces / VS Code remote container, $BROWSER is
// wired to a helper that opens a real tab on the user's machine, so
// any tool-call retry loop floods the host (and can OOM it). Force a
// no-op browser hook here. The explicit forwardPorts/openBrowser
// notification for port 8888 is independent of $BROWSER and still
// fires once when JupyterLab comes up.
"BROWSER": "/bin/true"
},
// Codespaces ports are private by default so skip the auth token.
// Need to disable XSRF check for Jupyter MCP servers if auth token is disabled.
"postStartCommand": "nohup start.sh jupyter lab --NotebookApp.token='' --ServerApp.disable_check_xsrf=True > /tmp/jupyter.log 2>&1 &"
}
38 changes: 38 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,41 @@ jobs:
tags: agent-client-kernel:ci
cache-from: type=gha
cache-to: type=gha,mode=max

pi-acp-e2e:
name: Pi ACP end-to-end integration test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: "22"

- name: Install pi and pi-acp
run: |
npm install -g \
@earendil-works/pi-coding-agent \
pi-acp
pi --version
test -x "$(which pi-acp)"

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
version: ">=0.9.15"
enable-cache: true

- name: Install project (locked, with dev extras)
run: uv sync --locked --extra dev

- name: Run pi-acp end-to-end tests
run: uv run pytest -v tests/e2e/test_pi_acp_integration.py
9 changes: 9 additions & 0 deletions .pi/agent/mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"mcpServers": {
"jupyter": {
"command": "uvx",
"args": ["jupyter-mcp-server@latest"],
"lifecycle": "lazy"
}
}
}
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ This project provides multiple devcontainer configurations for different ACP age
- `.devcontainer/goose/` - Block's Goose agent (Apache 2.0)
- `.devcontainer/kimi/` - MoonshotAI's Kimi CLI (Apache 2.0)
- `.devcontainer/cagent/` - Docker's cagent (Apache 2.0)
- `.devcontainer/pi/` - Pi coding agent via [pi-acp](https://github.com/svkozak/pi-acp) (MIT). Includes [pi-mcp-adapter](https://github.com/nicobailon/pi-mcp-adapter) and ships with the Datalayer [jupyter-mcp-server](https://github.com/datalayer/jupyter-mcp-server) preconfigured. Supports remote LLM API providers (Anthropic / OpenAI / Google / DeepSeek) and local providers (LM Studio / Ollama) — see `.pi/agent/models.json`.

**To use a devcontainer:**
1. Open the repository in GitHub Codespaces or VS Code with Dev Containers extension
Expand Down
Loading
Loading