Skip to content

Commit 4edf2a6

Browse files
authored
Merge pull request modelcontextprotocol#23 from jkoelker/jk/container
feat(container): add ghcr packaging pipeline
2 parents 1b81241 + 324d9e5 commit 4edf2a6

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

.github/workflows/container.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Container Image
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
tags:
9+
- "v*"
10+
11+
jobs:
12+
build-and-push:
13+
runs-on: ubuntu-latest
14+
permissions:
15+
contents: read
16+
packages: write
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Set up Docker Buildx
22+
uses: docker/setup-buildx-action@v3
23+
24+
- name: Log in to GHCR
25+
uses: docker/login-action@v3
26+
with:
27+
registry: ghcr.io
28+
username: ${{ github.actor }}
29+
password: ${{ secrets.GITHUB_TOKEN }}
30+
31+
- name: Extract metadata
32+
id: meta
33+
uses: docker/metadata-action@v5
34+
with:
35+
images: ghcr.io/${{ github.repository_owner }}/schwab-mcp
36+
tags: |
37+
type=ref,event=branch
38+
type=ref,event=tag
39+
type=semver,pattern={{version}}
40+
type=semver,pattern={{major}}.{{minor}}
41+
flavor: |
42+
latest=true
43+
44+
- name: Build and push
45+
uses: docker/build-push-action@v5
46+
with:
47+
context: .
48+
file: Containerfile
49+
push: ${{ github.event_name != 'pull_request' }}
50+
platforms: linux/amd64,linux/arm64
51+
tags: ${{ steps.meta.outputs.tags }}
52+
labels: ${{ steps.meta.outputs.labels }}

Containerfile

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# syntax=docker/dockerfile:1.7
2+
3+
FROM python:3.12-slim AS builder
4+
5+
ENV PYTHONDONTWRITEBYTECODE=1 \
6+
PYTHONUNBUFFERED=1
7+
8+
WORKDIR /app
9+
10+
# hadolint ignore=DL3008
11+
RUN apt-get update \
12+
&& apt-get install --no-install-recommends -y build-essential \
13+
&& rm -rf /var/lib/apt/lists/*
14+
15+
COPY pyproject.toml README.md ./
16+
COPY src ./src
17+
18+
# hadolint ignore=DL3013
19+
RUN pip install --no-cache-dir --upgrade pip \
20+
&& pip install --no-cache-dir build \
21+
&& python -m build --wheel --outdir /dist
22+
23+
FROM python:3.12-slim AS runtime
24+
25+
ENV PYTHONDONTWRITEBYTECODE=1 \
26+
PYTHONUNBUFFERED=1
27+
28+
# hadolint ignore=DL3008
29+
RUN apt-get update \
30+
&& apt-get install --no-install-recommends -y ca-certificates \
31+
&& rm -rf /var/lib/apt/lists/*
32+
33+
WORKDIR /workspace
34+
35+
COPY --from=builder /dist/ /tmp/dist/
36+
37+
RUN pip install --no-cache-dir /tmp/dist/*.whl \
38+
&& rm -rf /tmp/dist
39+
40+
LABEL org.opencontainers.image.title="Schwab MCP Server" \
41+
org.opencontainers.image.description="Model Context Protocol server for Schwab built on schwab-mcp." \
42+
org.opencontainers.image.source="https://github.com/jkoelker/schwab-mcp"
43+
44+
ENTRYPOINT ["schwab-mcp"]
45+
CMD ["server"]

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,26 @@ Discord-related flags can also be provided via environment variables:
9191
- `SCHWAB_MCP_DISCORD_TIMEOUT` (optional, defaults to 600 seconds)
9292
- `SCHWAB_MCP_DISCORD_APPROVERS` (optional comma-separated list of Discord user IDs)
9393

94+
### Container Image
95+
96+
Published container images are available at `ghcr.io/jkoelker/schwab-mcp`. The image runs `schwab-mcp server` by default and persists token data under `/root/.local/share/schwab-mcp` unless overridden.
97+
98+
```bash
99+
podman run --rm --interactive \
100+
--env SCHWAB_CLIENT_ID \
101+
--env SCHWAB_CLIENT_SECRET \
102+
--env SCHWAB_CALLBACK_URL \
103+
--env SCHWAB_MCP_DISCORD_TOKEN \
104+
--env SCHWAB_MCP_DISCORD_CHANNEL_ID \
105+
--publish 8182:8182 \
106+
--volume ~/.local/share/schwab-mcp:/schwab-mcp \
107+
ghcr.io/jkoelker/schwab-mcp:latest \
108+
server \
109+
--token-path /schwab-mcp/token.yaml
110+
```
111+
112+
The container entrypoint reads the same environment variables as the CLI, so you can override or add flags by appending them (for example, `... ghcr.io/jkoelker/schwab-mcp:latest --jesus-take-the-wheel`). To explore other entry points, run `docker run ghcr.io/jkoelker/schwab-mcp:latest --help`.
113+
94114
### Discord approval setup
95115

96116
1. Create or open your application at <https://discord.com/developers/applications>, add a **Bot**, reset the token, and paste it into `SCHWAB_MCP_DISCORD_TOKEN`.

0 commit comments

Comments
 (0)