Skip to content

Commit e2c8d2c

Browse files
committed
init
0 parents  commit e2c8d2c

442 files changed

Lines changed: 32516 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.dockerignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.git
2+
.gitignore
3+
.vscode
4+
.idea
5+
.docker
6+
.env
7+
.env.*
8+
9+
node_modules
10+
dist
11+
build
12+
13+
*.md
14+
*.log
15+
*.tmp
16+
*.temp
17+
18+
docker-compose.yml
19+
docker-compose.override.yml
20+
deploy
21+
postgres-init
22+
23+
Dockerfile
24+
.dockerignore
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: Build MCP Server Docker Image
2+
3+
on:
4+
push:
5+
tags: ["v*"]
6+
7+
env:
8+
REGISTRY: ghcr.io
9+
10+
jobs:
11+
build-and-push:
12+
runs-on: ubuntu-latest
13+
permissions:
14+
contents: read
15+
packages: write
16+
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
21+
- name: Verify package version matches tag
22+
run: |
23+
TAG_VERSION="${GITHUB_REF_NAME#v}"
24+
PKG_VERSION=$(jq -r .version mcp-server/package.json)
25+
if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
26+
echo "Tag $GITHUB_REF_NAME does not match mcp-server/package.json version ($PKG_VERSION)"
27+
exit 1
28+
fi
29+
30+
- name: Log in to GHCR
31+
uses: docker/login-action@v3
32+
with:
33+
registry: ${{ env.REGISTRY }}
34+
username: ${{ github.actor }}
35+
password: ${{ secrets.GITHUB_TOKEN }}
36+
37+
- name: Set up Docker Buildx
38+
uses: docker/setup-buildx-action@v3
39+
40+
- name: Docker metadata
41+
id: meta
42+
uses: docker/metadata-action@v5
43+
with:
44+
images: ${{ env.REGISTRY }}/${{ github.repository }}/mcp-server
45+
tags: |
46+
type=semver,pattern={{version}}
47+
type=semver,pattern={{major}}.{{minor}}
48+
type=semver,pattern={{major}}
49+
50+
- name: Build and push
51+
uses: docker/build-push-action@v6
52+
with:
53+
context: .
54+
push: true
55+
tags: ${{ steps.meta.outputs.tags }}
56+
labels: ${{ steps.meta.outputs.labels }}
57+
cache-from: type=gha
58+
cache-to: type=gha,mode=max

.github/workflows/run-tests.yml

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
name: Tests
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
workflow_dispatch:
7+
inputs:
8+
integration:
9+
description: Run integration tests (spins up Docker containers)
10+
type: boolean
11+
default: false
12+
13+
jobs:
14+
tests:
15+
name: Run typecheck, lint, and tests
16+
runs-on: ubuntu-latest
17+
18+
steps:
19+
- uses: actions/checkout@v4
20+
21+
- uses: pnpm/action-setup@v4
22+
23+
- uses: actions/setup-node@v4
24+
with:
25+
node-version: 22
26+
cache: pnpm
27+
28+
- name: Install dependencies
29+
run: pnpm install --frozen-lockfile
30+
31+
- name: Typecheck planka-api
32+
run: pnpm --filter planka-api typecheck
33+
34+
- name: Build
35+
run: pnpm build
36+
37+
- name: Typecheck mcp-server
38+
run: pnpm --filter mcp-server typecheck
39+
40+
- name: Lint
41+
run: pnpm lint
42+
43+
- name: Unit tests
44+
run: pnpm test:unit
45+
46+
- name: Integration tests
47+
if: ${{ inputs.integration == true }}
48+
run: pnpm test:integration

.gitignore

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Environment variables
2+
.env
3+
.env.local
4+
.env.*.local
5+
6+
# Node.js
7+
node_modules/
8+
npm-debug.log*
9+
yarn-debug.log*
10+
yarn-error.log*
11+
pnpm-debug.log*
12+
lerna-debug.log*
13+
14+
# Build outputs
15+
dist/
16+
build/
17+
out/
18+
19+
# Package manager files
20+
package-lock.json
21+
yarn.lock
22+
.pnpm-store/
23+
24+
# TypeScript
25+
*.tsbuildinfo
26+
.tsbuildinfo
27+
28+
# IDE
29+
.vscode/
30+
.idea/
31+
*.swp
32+
*.swo
33+
*~
34+
.DS_Store
35+
.AppleDouble
36+
.LSOverride
37+
*.iml
38+
*.ipr
39+
*.iws
40+
41+
# OS
42+
Thumbs.db
43+
Desktop.ini
44+
45+
# Logs
46+
logs/
47+
*.log
48+
49+
# Testing
50+
coverage/
51+
.nyc_output/
52+
*.lcov
53+
54+
# Temporary files
55+
tmp/
56+
temp/
57+
*.tmp
58+
*.temp
59+
60+
# Docker
61+
.docker/
62+
docker-compose.override.yml

CONTRIBUTING.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Contributing
2+
3+
## Prerequisites
4+
5+
- Node.js >= 22
6+
- pnpm
7+
- Docker + Docker Compose
8+
9+
## Setup
10+
11+
```bash
12+
pnpm install
13+
docker compose up -d
14+
docker compose exec postgres psql -U postgres -c 'CREATE DATABASE "charlieplan"'
15+
cp mcp-server/.env.example mcp-server/.env
16+
pnpm build
17+
```
18+
19+
## Development
20+
21+
```bash
22+
pnpm mcp:dev # run MCP server in dev mode (stdio transport)
23+
pnpm typecheck # type check all packages
24+
pnpm lint # lint with Biome
25+
pnpm lint:fix # auto-fix lint issues
26+
pnpm test:unit # unit tests
27+
pnpm test:integration # integration tests (requires Docker)
28+
```
29+
30+
## Regenerating the Planka API client
31+
32+
The `planka-api` package is generated from `planka-api/planka-swagger.json`.
33+
After editing the spec:
34+
35+
```bash
36+
pnpm planka:generate
37+
```
38+
39+
## Pull requests
40+
41+
- Keep PRs focused — one concern per PR.
42+
- Run `pnpm typecheck && pnpm lint && pnpm test:unit` before submitting.
43+
- Describe _why_ the change is needed, not just what it does.

Dockerfile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
FROM node:22-slim AS base
2+
ENV PNPM_HOME="/pnpm"
3+
ENV PATH="$PNPM_HOME:$PATH"
4+
RUN corepack enable
5+
6+
FROM base AS build
7+
WORKDIR /app
8+
COPY . .
9+
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
10+
11+
RUN pnpm --filter mcp-server... run build
12+
13+
RUN pnpm deploy --filter=mcp-server --prod /prod/mcp-server
14+
15+
FROM base
16+
ENV NODE_ENV=production
17+
WORKDIR /app
18+
COPY --from=build /prod/mcp-server .
19+
EXPOSE 3000
20+
CMD ["node", "dist/index-sse.js"]

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 ashkuc
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# charlieplan-mcp
2+
3+
MCP server that connects [Planka](https://planka.app) (self-hosted kanban) to AI assistants.
4+
5+
## Why this exists
6+
7+
AI chats are great for processing information — brainstorming, rewriting, distilling ideas from large amounts of text. But the output of that work — decisions, plans, next steps — needs to land somewhere.
8+
9+
Planka is a simple, self-hosted kanban board. This server connects it to your AI workflow: results from chats turn into cards, cards provide context for future chats. The board becomes a persistent layer between sessions.
10+
11+
![charlieplan-mcp](pepe-silvia-modified.png)
12+
13+
## What it does
14+
15+
Exposes Planka board management as MCP tools: projects, boards, lists, cards, labels, custom fields, task lists, attachments, and card search. Works with any MCP-compatible client (Claude Desktop, Claude.ai, etc.).
16+
17+
When `SERVER_URL` is configured, the server also enables OAuth and a `/redirect` endpoint — deep links that bounce from a card directly into a chat with pre-filled context.
18+
19+
## Local development
20+
21+
**Prerequisites:** Docker, Node.js ≥ 22, pnpm, Claude Code
22+
23+
```bash
24+
pnpm install
25+
```
26+
27+
```bash
28+
docker compose up -d
29+
```
30+
31+
```bash
32+
docker compose exec postgres psql -U postgres -c 'CREATE DATABASE "charlieplan"'
33+
```
34+
35+
```bash
36+
cp mcp-server/.env.example mcp-server/.env
37+
```
38+
39+
```bash
40+
pnpm build
41+
```
42+
43+
Open [http://localhost:1337](http://localhost:1337) — login `admin@example.com` / `admin123` → Profile → API Keys → create a key.
44+
45+
```bash
46+
claude mcp add planka -e PLANKA_API_KEY=<your-key> -- pnpm mcp:stdio
47+
```
48+
49+
Done. Migrations run automatically on first connection. Verify with `claude mcp list`.
50+
51+
## Self-hosting
52+
53+
See [deploy.md](deploy.md).

SECURITY.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Security Policy
2+
3+
## Reporting a Vulnerability
4+
5+
Please **do not** open a public GitHub issue for security vulnerabilities.
6+
7+
Report them privately via [GitHub Security Advisories](../../security/advisories/new)
8+
or by emailing the maintainer directly (see the GitHub profile).
9+
10+
Include:
11+
- A description of the vulnerability and its potential impact
12+
- Steps to reproduce or a proof-of-concept
13+
- Affected versions
14+
15+
You can expect an acknowledgement within 72 hours and a fix or mitigation plan
16+
within 14 days for confirmed issues.
17+
18+
## Scope
19+
20+
This project handles:
21+
- Planka API tokens passed as Bearer credentials
22+
- OAuth authorization codes (PKCE flow, signed with `CODE_SECRET`)
23+
- Per-card metadata stored in a local PostgreSQL database
24+
25+
Secrets are never logged or stored beyond their intended purpose.
26+
`CODE_SECRET` and Planka tokens are not persisted after use.

0 commit comments

Comments
 (0)