notion-mcp-remote
Notes
Project Page 1
-
notion-mcp-remote
project-notion-mcp-remotenotion-mcp-remote
Remote MCP server that brings Notion to claude.ai (and any MCP-over-HTTPS client) via Notion's OAuth 2.0 flow. A user grants the app access to their workspace once; the server then handles per-request workspace isolation via a ContextVar-based NotionClient so one deployment can serve many workspaces without token bleed.
Vision
Deliver a production-deployed, SOP-compliant notion-mcp-remote service that the operator can paste as a public URL into claude.ai's MCP settings, complete OAuth, and immediately use all 26 Notion tools in chat — no local stdio server, no integration-token juggling, no per-workspace redeploys. Success = OAuth round-trip under 60s and a
searchtool call returns live results from the authorized workspace on first try.User Stories
Key Story Note Role Success Metric claude-ai-connect story-notion-mcp-remote-claude-ai-connect claude.ai user (Lucas) Paste URL, complete OAuth in <60s, Notion tool works first try multi-workspace-safety story-notion-mcp-remote-multi-workspace-safety Operator Zero cross-tenant incidents; concurrency test returns workspace-correct results 100% ops-deploy-gitops story-notion-mcp-remote-ops-deploy-gitops Operator pre-deploy-validation checklist 100% green; zero manual kubectl edits post-deploy Architecture
- arch-domain-notion-mcp-remote — entities: DynamicClient, AuthCode, AccessToken, NotionWorkspace, User (erDiagram)
- arch-dataflow-notion-mcp-remote — DCR, OAuth handshake, tool-call sequence with ContextVar injection (sequenceDiagram)
- arch-deployment-notion-mcp-remote — Tailscale Funnel → ingress → pod, Harbor/Woodpecker/ArgoCD GitOps chain (graph TB)
Board
Primary kanban:
board-notion-mcp-remote.Columns: Backlog → Todo (review gate) → Next Up → In Progress → Done. Each ticket carries
story:{key}andarch:{component}labels for traceability.Status
- Local dev: working;
.venvinstalls deps,make runserves on :8000, OAuth tested end-to-end locally. - CI / k8s manifests: merged (commits 455b62c, 837e2f2, cb91a12). Woodpecker pipeline registry URL now points at internal cluster Harbor (
harbor-core.harbor.svc.cluster.local) perservice-onboarding-sop. Pipeline activation still pending (ticket #1048). - Prod deploy: PRs MERGED (services #72, overlay #138, both 2026-05-01). Local
k3s.tfvarsupdated with the new entry.tofu planran 2026-05-03 — paused at operator review: 7 expected new resources cleanly identified, but 14 unrelated drift items surfaced (pre-existing label drift on harbor_creds across 7 namespaces + provider write-only attr migration). Per PR #72 test plan, escalated rather than auto-applied. See pal-e-platform#296 comment for full plan output + 3 decision options. - claude.ai integration: not yet; blocked on public URL (#1049 is the E2E verification ticket).
- Discovered scope: spike #11 — auto-bump remote-base
?ref=SHA via Woodpecker (mirroring Image Updater pattern). Plus 2 doc updates queued for next /update-docs cycle:convention-kustomize-overlayRemote Base Variant section andsop-validation3-PR merge-order documentation. Plus 1 platform-board ticket pending: audit + reconcile pal-e-services drift (14 items).
Milestones
- No milestones yet. Target M1: first prod deploy with public URL reachable from claude.ai (drives all three user stories).
Repos
Repo Platform Role Status notion-mcp-remote Forgejo the service — FastMCP app, OAuth proxy, k8s manifests active mcp-remote-auth Forgejo shared OAuth infrastructure (consumed as mcp-remote-auth-ldraney)active, v0.1.0 on public PyPI notion-mcp Forgejo / PyPI upstream tool surface (consumed as notion-mcp-ldraney)active, v0.1.13 pal-e-services Forgejo terraform service registration ( var.services)pending PR pal-e-deployments Forgejo kustomize overlay for prod pending PR
Review 15
-
Review 3: feat: add overlays/notion-mcp-remote/prod kustomize overlay
review-1044-2026-04-21-v3Verdict: APPROVED
Third review of board item #1044 (Forgejo
forgejo_admin/pal-e-deployments#132) after micro-fix round addressing the two residuals fromreview-1044-2026-04-21-v2. Both fixes landed cleanly in the live issue body. Ticket is ready to advance frombacklog.Verification of Micro-Fixes
# Fix Requested (v2) Landed? Evidence in live issue body 1 [BODY]Remote-base URL.git/k8s→.git//k8s(blocker -- kustomize requires//separator)YES Context section now shows https://forgejo.tail5b443a.ts.net/forgejo_admin/notion-mcp-remote.git//k8s?ref=maininside a fenced code block. Double-slash verified by direct string search of the issue body.2 [BODY]Drop AC #4 (Image Updater annotations inkustomization.yaml) -- annotations live on the ArgoCD Application, not the overlayYES AC list now has 6 checkboxes (down from 7): (1) kubectl kustomizerenders clean, (2)resources:remote URL, (3)images:transformer with SHA-regexnewTag, (4) nostringData/data, (5) no env/envFrom redefinition, (6) PR reviewed and merged. No mention of "annotations present inkustomization.yaml" anywhere in the AC section.Refinement Side-Effect Check
Spot-checked that the micro-fix did not regress anything else:
- File Targets still references "the pinned remote URL above" -- correct single-source-of-truth factoring, no stale URL left in that section.
- Context "Base factoring decision" still present with correct rationale (remote-base, not
bases/standard/). - Secrets subsection unchanged: base wires env via
valueFrom: secretKeyRef: name: notion-mcp-secretswith kebab-case keys; overlay must not override. - Ingress subsection unchanged: Funnel provisioned by pal-e-services terraform, no
ingress.yamlin overlay. - Sibling ticket reference
forgejo_admin/notion-mcp-remote#7(board item #1047) intact in Context and Related. - Related section still lists
convention-kustomize-overlay,arch-deployment-notion-mcp-remote,project-notion-mcp-remote,service-onboarding-sop,story-notion-mcp-remote-ops-deploy-gitops. - Checklist still enforces SHA regex
^[0-9a-f]{7,40}$(notlatest). - Issue remains open; labels on the board item preserved:
type:feature,story:ops-deploy-gitops,arch:kustomize-overlay.
Template Completeness
- [x] Type (Feature)
- [x] Lineage
- [x] Repo
- [x] User Story
- [x] Context
- [x] File Targets
- [x] Acceptance Criteria (6 items)
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x]
story:ops-deploy-gitopslabel present; story entry exists onproject-notion-mcp-remoteuser-stories - [x]
arch:kustomize-overlaylabel present; backing docconvention-kustomize-overlay - [x]
type:featurematches Type header - [x] Forgejo issue
forgejo_admin/pal-e-deployments#132open
File Targets
- [x]
overlays/notion-mcp-remote/prod/kustomization.yaml-- new file, parent path aligns with sibling overlays - [x]
overlays/notion-mcp-remote/prod/deployment-patch.yaml-- new file, standard overlay pattern - [x] Remote base URL verified renderable:
forgejo_admin/notion-mcp-remote.git//k8s?ref=mainwith correct//separator
Repo Placement
Correct. Overlay created in
pal-e-deployments; no cross-repo scope creep.Dependencies
- Sibling #1047 (
forgejo_admin/notion-mcp-remote#7) -- Secretnotion-mcp-secrets, not blocking overlay merge - Downstream #1045 (pal-e-platform#296) -- ArgoCD Application
- Downstream #1043 (pal-e-services#57) --
var.servicesentry withsource_path = "overlays/notion-mcp-remote/prod"
Acceptance Criteria
All 6 AC now testable and unambiguous. Previous AC #4 ambiguity (annotations in overlay vs Application) is resolved by removal. Image Updater write-back is covered by AC #3's SHA-regex constraint on
newTag, which is sufficient because Image Updater's write-back to kustomization requires only theimages:entry and the SHA format -- the annotations that drive the write-back live on the ArgoCD Application (provisioned by ticket #1043 via pal-e-services terraform).Blast Radius
Low. Overlay inert until #1045 (ArgoCD Application) points at it AND #1047 (Secret) exists. Rollback:
git revert.Decomposition Assessment
3 points, 2 files, 1 repo, 6 AC. Fits single agent pass (<5 min). No decomposition needed.
Recommendation
No action needed. Ticket is APPROVED.
- Tag board item #1044 with
review,ready. - Ticket eligible to advance
backlog → todopersop-board-workflowon next ops pass.
-
Review 2: feat: add overlays/notion-mcp-remote/prod kustomize overlay
review-1044-2026-04-21-v2Verdict: NEEDS_REFINEMENT
Re-review of board item #1044 (Forgejo
forgejo_admin/pal-e-deployments#132) after refinement. Six of seven fixes fromreview-1044-2026-04-21landed cleanly. One new issue found: the remote-base kustomize URL uses single-slash form, which kustomize cannot parse -- it requires//as the separator between git repo root and subpath. This will fail atkubectl kustomizetime.Template Completeness
- [x] Type (Feature)
- [x] Lineage
- [x] Repo
- [x] User Story
- [x] Context
- [x] File Targets
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
Verification of Prior Fixes
# Fix Requested Landed? Evidence 1 Base factoring: remote base referencing service repo's k8s/YES Context explicitly picks remote-base pattern, gives URL, rejects bases/standard/. File Targets "Files NOT to touch" excludesbases/notion-mcp-remote/.2 Remove envFrom; base uses valueFrom: secretKeyRef: name: notion-mcp-secretsYES Context "Secrets" subsection states base already wires env via valueFrom: secretKeyRef: name: notion-mcp-secretswith kebab-case keys and instructs overlay not to override. AC includes "Overlay does not redefine env vars orenvFrom".3 Name sibling ticket #7 / board #1047 explicitly YES Context names forgejo_admin/notion-mcp-remote#7(board item #1047); Related section repeats it.4 Remove ingress.yaml(Funnel via pal-e-services terraform)YES Context "Ingress" subsection states Funnel is provisioned by pal-e-services terraform and forbids ingress.yaml. Noingress.yamlin File Targets.5 Replace newTag: latestwith hex SHAYES (placeholder) File Targets specifies "a bootstrap newTagof a real commit SHA (placeholder<7-40 hex chars, first SHA from notion-mcp-remote main>)"; Checklist requires SHA matching^[0-9a-f]{7,40}$, notlatest.6 AC adjustments: drop envFrom AC, drop container-port AC, add images: transformer AC YES envFrom AC removed; container-port AC removed; new AC for images:transformer withname: harbor.tail5b443a.ts.net/notion-mcp-remote/notion-mcp-remoteand SHAnewTag.7 Body references convention-kustomize-overlayandarch-deployment-notion-mcp-remoteYES Lineage cites convention-kustomize-overlay; Context citesarch-deployment-notion-mcp-remote; both listed in Related.Traceability
- [x]
story:ops-deploy-gitopslabel -- verified present on board item; story entry inproject-notion-mcp-remoteuser-stories - [x]
arch:kustomize-overlaylabel -- present; backing note isconvention-kustomize-overlay. Prior review noted [SCOPE] ambiguity (convention vs arch); not blocking this review. - [x]
type:featurelabel matches Type header - [x] Forgejo issue open (
forgejo_admin/pal-e-deployments#132)
File Targets
- [x]
overlays/notion-mcp-remote/prod/kustomization.yaml-- new file; parent path aligns with sibling overlays (overlays/pal-e-docs/prod/, etc.) - [x]
overlays/notion-mcp-remote/prod/deployment-patch.yaml-- new file; standard overlay pattern perconvention-kustomize-overlay - [x] Remote base source verified:
forgejo_admin/notion-mcp-remote/k8s/on main contains deployment.yaml, service.yaml, pvc.yaml, servicemonitor.yaml, kustomization.yaml. Base deployment image:harbor.tail5b443a.ts.net/notion-mcp-remote/notion-mcp-remote:latest. Base secret wiring usesvalueFrom: secretKeyRef: name: notion-mcp-secretswith kebab-case keys. All matches issue body claims.
New Issue: Remote-Base URL Format (BLOCKER for dev agent)
The issue body specifies the remote kustomize
resources:URL as:https://forgejo.tail5b443a.ts.net/forgejo_admin/notion-mcp-remote.git/k8s?ref=mainKustomize's remote-source syntax requires a
//separator between the git repository URL and the subdirectory within the repo. With single-slash form, kustomize will either try to clone.../notion-mcp-remote.git/k8sas the repo URL (fails) or parse the entire path as the repo with no subpath. Sibling overlays inpal-e-deploymentsdon't use remote bases (they use filesystem paths../../../bases/standard), so there's no in-repo exemplar, but kustomize behaviour is consistent on this.Correct form:
https://forgejo.tail5b443a.ts.net/forgejo_admin/notion-mcp-remote.git//k8s?ref=mainNote the
.git//k8s(double slash). This syntax is required forkubectl kustomize overlays/notion-mcp-remote/prod/to render clean, which is AC #1. Without this fix, the dev agent will either (i) hit a render error, (ii) "fix" the URL themselves and guess, or (iii) silently use a different factoring. One-character BODY edit avoids all three.Sanity Check: images Transformer vs Real Harbor Path
Base deployment (fetched from
notion-mcp-remote/k8s/deployment.yamlon main) uses imageharbor.tail5b443a.ts.net/notion-mcp-remote/notion-mcp-remote:latest.AC specifies images transformer with
name: harbor.tail5b443a.ts.net/notion-mcp-remote/notion-mcp-remote. Matches exactly. Because the overlay consumes the base directly (not vianame: appplaceholder as inbases/standard), only ONE images entry is needed (not two as inoverlays/pal-e-docs/prod/, which needs bothapp-imageand the Harbor URL). AC correctly reflects this.Repo Placement
Correct. Issue filed on
pal-e-deployments, all File Targets underoverlays/notion-mcp-remote/prod/*.Dependencies
- [pending] Sibling secret-provisioning ticket --
forgejo_admin/notion-mcp-remote#7(board item #1047) must create Secretnotion-mcp-secretswith keysbase-url,oauth-client-id,oauth-client-secret,session-secret,onboard-secret. Not blocking overlay merge (inert until ArgoCD syncs). - [pending] Downstream -- pal-e-platform#296 (board item #1045) creates ArgoCD Application.
- [pending] Downstream -- pal-e-services#57 (board item #1043) adds
var.servicesentry withsource_path = "overlays/notion-mcp-remote/prod". Overlay path is contractual with this ticket. - All dependencies documented in Context and Related sections of the issue body.
Acceptance Criteria
- AC #1 (
kubectl kustomizerenders clean) -- achievable ONCE the//URL fix lands. Network access to tailnet required to fetch remote base. - AC #2 (
resources:references service repok8s/pinned to main) -- testable via file inspection. - AC #3 (
images:transformer with correct name + SHA-regexnewTag) -- testable via file inspection + regex. - AC #4 (Image Updater annotations present in
kustomization.yaml) -- AMBIGUOUS. Perconvention-kustomize-overlay"Terraform Integration", Image Updater annotations live on the ArgoCD Application (provisioned by pal-e-services terraform withsource_reposet), not on the overlay'skustomization.yaml. The overlay's role is to exposeimages:entries that Image Updater rewrites (pluswrite-back-target: kustomizationon the Application). Inspection ofoverlays/pal-e-docs/prod/kustomization.yamlconfirms: no Image Updater annotations in the overlay. AC #4 as written may be unachievable in a literal reading. Suggest rephrasing to "images:transformer written in form compatible with Image Updater write-back (regex^[0-9a-f]{7,40}$)" -- already covered by AC #3. Non-blocking for this review since an astute dev agent can infer intent, but ideally clarified. - AC #5 (no
stringData/datasecret values) -- testable via grep. - AC #6 (overlay does not redefine env/envFrom) -- testable by diffing rendered output against base.
Blast Radius
Low. Single overlay directory in pal-e-deployments; no change to other services' overlays or
bases/. Overlay is inert until pal-e-services ArgoCD Application (#1045) points at it AND Secret exists (#1047). Cannot break prod by merging alone. Rollback isgit revert.Decomposition Assessment
3 points, 2 files, 1 repo, 6 AC. Fits single agent pass (<5 min). No decomposition needed.
Recommendation
One fix remaining:
[BODY]Correct the remote-base kustomize URL to use//as the separator between the.gitrepo root and thek8ssubpath. Change both occurrences (in Context section and File Targets section) fromhttps://forgejo.tail5b443a.ts.net/forgejo_admin/notion-mcp-remote.git/k8s?ref=maintohttps://forgejo.tail5b443a.ts.net/forgejo_admin/notion-mcp-remote.git//k8s?ref=main.
Optional non-blocking:
[BODY]Clarify AC #4 ("ArgoCD Image Updater annotations present") -- perconvention-kustomize-overlaythese annotations live on the ArgoCD Application (via pal-e-services terraform), not on the overlay'skustomization.yaml. Suggest dropping AC #4 (redundant with #3) or rewording to reference theimages:entry format only.
Once the URL fix lands, verdict will be APPROVED and the board item can be tagged
review,ready. -
Review: tofu apply to provision notion-mcp-remote (re-review)
review-1045-2026-04-21-v2Verdict: APPROVED
Re-review after label fix. The single gap from
review-1045-2026-04-21(arch:argocdwith no backing note) is resolved. Main session relabeled board item 1045 toarch:deployment-notion-mcp-remote, which has a backing active architecture note (arch-deployment-notion-mcp-remote, id 1548). Ticket is apply-ready pending the two documented upstream dependencies.Fix Verification
- [x] Board item 1045 labels confirmed via
list_board_items:type:feature,story:ops-deploy-gitops,arch:deployment-notion-mcp-remote(updated 2026-04-21T17:42:44) - [x]
arch-deployment-notion-mcp-remotenote exists in pal-e-docs (status: active, type: architecture, project: notion-mcp-remote) - [x] Ticket body already references
arch-deployment-notion-mcp-remote— the label now matches the body's architectural anchor - [x] Note:
search_notesreturned empty for both exact-slug and fuzzy queries; verified via directget_note(slug=...)which is authoritative. Search indexing lag, not a real gap.
Template Completeness
- [x] Type (Feature)
- [x] Lineage
- [x] Repo (forgejo_admin/pal-e-platform)
- [x] User Story
- [x] Context
- [x] File Targets (no-file-change apply operation; correctly scoped)
- [x] Acceptance Criteria
- [x] Test Expectations (tofu plan command)
- [x] Constraints (lock-false, no -target, Lucas approval, pre-deploy checklist)
- [x] Checklist
- [x] Related
Traceability
- [x] story:ops-deploy-gitops label — "GitOps Deploy via pal-e Platform"
- [x] story note verified — story-notion-mcp-remote-ops-deploy-gitops listed in project-notion-mcp-remote user-stories section
- [x] arch:deployment-notion-mcp-remote label present on board item
- [x] arch note verified — arch-deployment-notion-mcp-remote (id 1548) active in pal-e-docs; label now matches ticket body anchor
- [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/forgejo_admin/pal-e-platform/issues/296 open and valid
File Targets
No file changes — this is the apply operation itself. "Files NOT to touch" correctly lists network-policies.tf. No file-target verification applicable.
Repo Placement
OK. Apply runs against forgejo_admin/pal-e-platform terraform. Sibling tickets correctly scoped (pal-e-services #57, pal-e-deployments #132, notion-mcp-remote #7 for secrets).
Dependencies
Unchanged from prior review. Documented and sequenced correctly:
- pal-e-services #57 (board 1043, backlog, 3 pts) — MUST LAND FIRST
- pal-e-deployments #132 (board 1044, backlog, 3 pts) — MUST LAND FIRST
- notion-mcp-remote #7 (board 1047, backlog, 2 pts) — MUST LAND BEFORE FIRST ARGOCD SYNC
Acceptance Criteria
Agent-verifiable: tofu plan diff inspection, no unexpected destructive changes, Lucas approval gate,
kubectl get ns notion-mcp-remote, ArgoCD Application Healthy + Synced, Funnel URL reachable.Blast Radius
Contained first-deploy. No shared-infra changes. Lock-false plan review + Lucas gate mitigates any surprise diffs per sop-platform-tf-changes.
Decomposition Assessment
No decomposition needed. Single apply with one plan-review gate. Fits 5-minute rule.
Recommendation
No action needed. Ticket is apply-ready once dependencies #57 and #132 merge and the pre-deploy validation checklist is green. Advance to next_up when scheduling permits.
- [x] Board item 1045 labels confirmed via
-
Re-review: var.services entry for notion-mcp-remote (ArgoCD discovery)
review-1043-2026-04-21-v2Verdict: APPROVED
Board item: #1043 on
board-notion-mcp-remote— Forgejo issueforgejo_admin/pal-e-services#57. Type: Feature. Points: 3. Labels:type:feature,story:ops-deploy-gitops,arch:argocd.Prior review:
review-1043-2026-04-21(NEEDS_REFINEMENT) flagged four fixes. This re-review verifies each landed.Refinement Verification
# Fix Requested Landed? Evidence 1 Schema: source_repomust beforgejo_admin/pal-e-deployments(owner prefix)YES Context section documents owner-prefixed value. Schema note block explains why (services.tf URL interpolation as http://forgejo-http.forgejo.svc.cluster.local/${coalesce(source_repo, forgejo_repo)}.git). AC2 asserts the exact value. Verified against liveterraform/variables.tf(services object type:source_repo = optional(string)),terraform/services.tf(argocd_application.service repo_url coalesces source_repo/forgejo_repo), andterraform/k3s.tfvars.example(example uses owner-prefixed form).2 Remove speculative "Possibly terraform/services.tf" File TargetYES File Targets reduced to terraform/k3s.tfvarsonly.terraform/services.tfmoved under "Files NOT to touch" with justification: module already supports per-servicefunnel=trueviakubernetes_ingress_v1.service_funnelwithfor_each = { for k, v in var.services : k => v if v.funnel }— verified in live services.tf.3 Workflow: pal-e-services is plan-and-apply-BEFORE-merge per sop-platform-tf-changes(NOT apply-on-merge)YES Constraints section leads with "Workflow: plan-and-apply-BEFORE-merge" bullet, cites sop-platform-tf-changes, explicitly contrasts with pal-e-platform's CI-driven apply-on-merge, and prescribes the sequence (open PR → review → plan → apply → verify → merge). Matchessop-platform-tf-changes§ standard-workflow §§ pal-e-services block exactly.4 Blast-radius guard: verify tofu plandiff isolated to new resources; halt on driftYES Constraints section has dedicated "Blast-radius guard" bullet. Names expected resources (namespace, Harbor project + robots, pull secret, ArgoCD application, funnel ingress). Instructs STOP/do-not-apply/escalate on drift. Cites precedent review-1064-2026-04-20pal-e-services state-drift incident. Duplicated in Checklist as "tofu plan reviewed; diff isolated to notion-mcp-remote resources".Template Completeness
- [x] Type (Feature)
- [x] Lineage
- [x] Repo
- [x] User Story
- [x] Context (with Schema note and explicit key values)
- [x] File Targets (+ explicit "Files NOT to touch")
- [x] Acceptance Criteria (5 items, all verifiable)
- [x] Test Expectations (with run command)
- [x] Constraints (workflow + blast-radius + style)
- [x] Checklist
- [x] Related Architecture (references
arch-deployment-notion-mcp-remote) - [x] Related
Traceability
- [x]
story:ops-deploy-gitopslabel — maps tostory-notion-mcp-remote-ops-deploy-gitops - [x] story note verified — listed in
project-notion-mcp-remoteuser-stories table (confirmed in prior review) - [x]
arch:argocdlabel — component: ArgoCD - [ ] arch note
arch-argocdMISSING — [SCOPE] carried over from prior review.get_note(slug="arch-argocd")returns 404. This remains a backing-note gap but does not block ticket advancement (perskill-review-ticket, [SCOPE] items flag follow-up work without requiring NEEDS_REFINEMENT). Related Architecture pointerarch-deployment-notion-mcp-remoteis present and verified. - [x] arch note
arch-deployment-notion-mcp-remoteverified — exists, active, documents the service topology includingpal-e-services var.services --> ACDedge this ticket implements - [x] Forgejo issue —
forgejo_admin/pal-e-services#57, open
File Targets
- [x]
terraform/k3s.tfvars— correct. File is gitignored (expected; plan-and-apply-before-merge pattern). - [x] "Files NOT to touch" list — correctly enumerates
terraform/services.tf,network-policies.tf, other service entries. Prevents scope creep.
Repo Placement
Correct. Issue on
forgejo_admin/pal-e-serviceswhere schema and tfvars live.Dependencies
Unchanged from prior review. Upstream blocker
pal-e-deployments#132(overlay, board #1044) correctly documented. Downstreampal-e-platform#296(board #1045) no longer required per the corrected workflow — apply is manual on pal-e-services, so pal-e-platform is not implicated.Acceptance Criteria
Five ACs, all verifiable:
- AC1 (entry present) — grep-verifiable
- AC2 (
source_repoowner-prefixed) — NEW; grep-verifiable and directly addresses prior review's fix #1 - AC3 (plan diff shows expected resources, no unexpected diff) — runnable; resource list explicit
- AC4 (values match pre-deploy-validation checklist) — references
service-onboarding-sop - AC5 (PR reviewed and merged) — process criterion
Blast Radius
Now explicitly documented in the ticket itself with STOP/escalate procedure and precedent citation. The original concern is now the agent's responsibility to verify in-flight, as it should be.
Decomposition Assessment
1 file edit (one map entry in k3s.tfvars), 5 acceptance criteria, estimated agent work ~5-10 min including plan review. No decomposition needed. 3 points remains reasonable.
Recommendation
All four refinement fixes landed and verified against live codebase. Ticket is ready to advance from
backlog.- No action needed on the ticket body.
[SCOPE]carry-over:arch-argocdnote still missing. Recommend filing separately — do not block #1043 on it. Same pattern asarch-tailscale-funnelgap flagged inreview-788-2026-04-03; these platform-level arch notes are a documentation backlog concern, not a per-ticket blocker.
Related
review-1043-2026-04-21— prior review (NEEDS_REFINEMENT)forgejo_admin/pal-e-services#57— issuesop-platform-tf-changes— workflow authorityreview-1064-2026-04-20— state-drift precedent cited in blast-radius guardarch-deployment-notion-mcp-remote— related architecture
-
Re-review: E2E verify + claude.ai integration for notion-mcp-remote
review-1049-2026-04-21-v2Verdict: APPROVED
Re-review of board item #1049 after label swap. Prior review
review-1049-2026-04-21returned NEEDS_REFINEMENT with one blocking[SCOPE]item (orphanarch:tailscale-funnellabel) and one optional[BODY]nit. The label was swapped toarch:deployment-notion-mcp-remote. Traceability gap is resolved. Body is otherwise strong. Ticket is ready to advance.Changes Since Prior Review
- Board item 1049 labels now:
type:feature,story:claude-ai-connect,arch:deployment-notion-mcp-remote(confirmed vialist_board_items, updated_at=2026-04-21T17:42:44). - Prior
arch:tailscale-funnellabel has been removed — no longer an orphan.
Traceability
- [x]
story:claude-ai-connectlabel — story "Connect Notion to claude.ai" - [x] story note verified —
story-notion-mcp-remote-claude-ai-connectlisted inproject-notion-mcp-remoteuser-stories section (unchanged from prior review) - [x]
arch:deployment-notion-mcp-remotelabel — deployment architecture for the service - [x] arch note verified —
arch-deployment-notion-mcp-remoteexists in pal-e-docs, status=active, note_type=architecture. Contains the full deployment topology including the Tailscale Funnel leg (notion-mcp-remote.tail5b443a.ts.net), Harbor, Woodpecker, ArgoCD + Image Updater, Kustomize overlay, and PVC. Covers every component exercised by the E2E validation AC. - [x]
type:featurelabel — appropriate (new capability validation, not a bug fix) - [x] Forgejo issue — forgejo_admin/notion-mcp-remote#9 — open, body unchanged and template-compliant
Template Completeness
Unchanged from prior review — all 11 template sections present (Type, Lineage, Repo, User Story, Context, File Targets, Acceptance Criteria, Test Expectations, Constraints, Checklist, Related). 11 AC map cleanly to the 6 user-story AC plus platform and recordkeeping checks.
File Targets
Not applicable — verification-only ticket. Deliverables are a validation note (per
sop-validation) and a milestone mark on the project page Status section.Repo Placement
OK. Issue filed on
forgejo_admin/notion-mcp-remote, which owns the validation pass. No change.Dependencies
Seven prerequisite tickets still in backlog (pal-e-platform#296, pal-e-services#57, pal-e-deployments#132, notion-mcp-remote#5/#6/#7/#8). Correctly documented in the Constraints section ("Do not begin this ticket until all upstream tickets closed"). The review→todo gate is independent of the execution gate; this ticket can advance to
todoonce upstream work is ready to start. No refinement needed.Acceptance Criteria
11 AC unchanged and testable with concrete commands. Platform checks cover Woodpecker pipeline, ArgoCD sync+health, pod Running,
/health200,/.well-known/oauth-authorization-serverJSON shape, and Prometheus scrape ofnotion_mcp_remote_up. E2E checks cover all 6 story AC (claude.ai URL accept, Notion OAuth < 60s, search returns real results, second tool call succeeds). Recordkeeping covers validation note + Milestone M1 mark + upstream closure.The prior optional
[BODY]nit about assertingregistration_endpointon the/.well-knownAC is non-blocking and intentionally not required. Dynamic Client Registration will be implicitly exercised by the E2E AC "claude.ai accepts the connector URL" — if DCR were broken, that AC would fail. The optional nit stands as a future quality improvement but does not gate approval.Blast Radius
Small — observational only, no config or code changes. Unchanged from prior review.
Decomposition Assessment
11 AC exceeds the default 5-AC threshold, but validation tickets are explicitly exempt — one pass must exercise every AC of the user story per
sop-validation. Points=2 reflects the 15–20 min execution. No decomposition needed.Recommendation
Verdict: APPROVED. No action needed. Ticket is ready to advance from
backlogtotodowhen upstream dependencies are cleared.- Prior
[SCOPE]— resolved by label swap toarch:deployment-notion-mcp-remote; backing arch note verified present. - Prior
[BODY]nit (registration_endpointassertion) — acknowledged, non-blocking, not required to prove AC. DCR failure mode is caught by the existing "claude.ai accepts URL" E2E AC.
Related
- Board item 1049 —
board-notion-mcp-remotecolumn=backlog - Forgejo:
forgejo_admin/notion-mcp-remote#9 - Prior review:
review-1049-2026-04-21(NEEDS_REFINEMENT — superseded) arch-deployment-notion-mcp-remotestory-notion-mcp-remote-claude-ai-connectproject-notion-mcp-remotesop-validation
- Board item 1049 labels now:
-
Re-Review: Register Notion OAuth app
review-1046-2026-04-21-rereviewVerdict: APPROVED
Ticket: board item #1046 —
forgejo_admin/notion-mcp-remote#6— feat: register public Notion OAuth integration for remote proxyPrior review:
review-1046-2026-04-21(NEEDS_REFINEMENT). This is a targeted re-review checking only the three gaps flagged by the original reviewer.Gap Verification
- [x]
[BODY]Redirect URI/callback→/oauth/callback— CLOSED. AC #3 now readshttps://notion-mcp-remote.tail5b443a.ts.net/oauth/callback. AC #5 smoke curl uses the URL-encoded form%2Foauth%2Fcallback. Constraints cite the source of truth:mcp_remote_auth/routes.py:41andprovider.py:125,151. - [x]
[BODY]/onboard/callbackfollow-up consideration — CLOSED (as follow-up flag, not blocker). Constraints section now includes: "Follow-up: when the onboarding flow story lands, decide whether to register/onboard/callbackas a secondary redirect URI on this same integration (avoids a second UI trip) or create a separate integration." Appropriate disposition — the onboarding flow is a separate story, so registering the URI now is optional and correctly deferred with a clear decision point. - [x]
[SCOPE]Referencearch-dataflow-notion-mcp-remote— CLOSED. Related section explicitly linksarch-dataflow-notion-mcp-remotewith the(OAuth handshake sequence)annotation. Confirmed the backing note exists (id 1547, active, architecture note). The "stale link" flag from the original gap list turned out to be a non-issue as reported — there was no stale link to replace, and the dataflow note is now properly cited as the arch reference.
Template Completeness
Unchanged from prior review — all sections of
template-issue-featurestill present and well-formed. No sections were dropped during refinement.Traceability
- [x]
story:claude-ai-connectlabel — Connect Notion to claude.ai, story note verified (id 1543). - [x]
arch:notion-oauthlabel — body now resolves this to the existingarch-dataflow-notion-mcp-remotenote (id 1547) which documents the OAuth handshake sequence. Traceability link is satisfied in the body even though a dedicatedarch-notion-oauthnote does not exist under that exact slug; Ava previously indicated this resolution (citing the dataflow note) is acceptable. - [x] Forgejo issue —
forgejo_admin/notion-mcp-remote#6, open.
File Targets
No repo file edits expected — credential provisioning ticket. Downstream consumers (
server.py:19-20,.env.example:2-3,k8s/deployment.yaml:43-52) unchanged since prior review and re-verified against the current working tree. "Do not touch" list accurate.Repo Placement
OK — issue is filed on
forgejo_admin/notion-mcp-remotewhere the credentials are consumed. Sibling tickets (#7 secret provisioning, #9 E2E verify) correctly separated into their own issues.Dependencies
Unchanged: this ticket blocks #7 (
arch:k8s-secret) and #9 (arch:tailscale-funnel). Not blocked by anything currently inin_progress. Safe to advance.Acceptance Criteria
All 5 ACs verifiable. AC #3 and AC #5 now testable against the real
/oauth/callbackendpoint. The redirect URI path fix closes the critical silent-failure mode flagged in the prior review.Blast Radius
Risk profile improved from the prior review: the wrong-path silent-failure mode is eliminated by the AC #3/#5 fix. Remaining risks are the same as any OAuth credential provisioning — CLIENT_SECRET must stay out of git (Constraint #3 covers this), and BASE_URL / Tailscale Funnel hostname changes will require re-editing the Notion integration in the UI. Nothing new introduced by the refinement.
Decomposition Assessment
1 point, 5 ACs, no code edits, single UI flow. Well within the 5-minute rule. No decomposition needed.
Recommendation
All flagged gaps are addressed. Ticket is ready to move
backlog → todo → next_up. No action needed. - [x]
-
Review: Provision k8s secrets for notion-mcp-remote (re-review)
review-1047-2026-04-21-v2Verdict: APPROVED
Re-review of board item #1047 / Forgejo issue forgejo_admin/notion-mcp-remote#7 after refinement. Prior verdict:
review-1047-2026-04-21NEEDS_REFINEMENT. All flagged gaps have been resolved. Type: Feature. Points: 2.Refinement Gaps Closed
The prior review flagged a
[BODY]gap: the AC secret name/shape did not match the deployment manifest on main. Verified against/home/ldraney/notion-mcp-remote/k8s/deployment.yaml:- [x] Secret name corrected to
notion-mcp-secrets(matches manifest line 41, 46, 51, 56, 61) - [x] Keys rewritten in kebab-case:
base-url,oauth-client-id,oauth-client-secret,session-secret,onboard-secret(matches manifest lines 42, 47, 52, 57, 62 exactly) - [x] Wiring model changed to
valueFrom: secretKeyRef;envFromlanguage removed from Context - [x] Mapping table added: secret key → env var → consumer (matches manifest env block lines 38-62)
- [x] AC strengthened: now requires
kubectl rollout status,kubectl exec envverification of all 5 env vars inside the container, and a/health200 probe - [x] Explicit constraint added: "Do not normalise to UPPER_SNAKE_CASE;
secretKeyRef.keylookups are case-sensitive"
Template Completeness
- [x] Type (Feature)
- [x] Lineage
- [x] Repo
- [x] User Story (role / want / so-that)
- [x] Context (now internally consistent with the deployment)
- [x] File Targets (correctly "no repo file changes")
- [x] Acceptance Criteria (7 items, each verifiable)
- [x] Test Expectations (runnable kubectl commands)
- [x] Constraints (4 guardrails including the new case-sensitivity callout)
- [x] Checklist (5 items)
- [x] Related (project, story, SOP, arch note)
Traceability
- [x]
story:ops-deploy-gitopslabel — GitOps Deploy via pal-e Platform - [x] story note verified —
story-notion-mcp-remote-ops-deploy-gitopsexists; AC row "Secrets provisioned viakubectl create secret genericbefore first ArgoCD sync" maps 1:1 - [x]
arch:k8s-secretlabel — architectural coverage present inarch-deployment-notion-mcp-remoteandarch-secrets-pipeline. Prior review's optional[LABEL]suggestion was not actioned, but it was optional ("interpretable but inconsistent") — not a blocker. - [x] Forgejo issue — URL valid, #7 open, body well-formed
File Targets
No repo file changes — this is a
kubectloperation. Cross-checked manifest for shape alignment:- [x]
k8s/deployment.yamllines 38-62 — verified all 5 env blocks usevalueFrom.secretKeyRef.name: notion-mcp-secretswith the exact kebab-case keys the AC now lists. Env var names matchserver.pyconsumers.
Repo Placement
OK. Issue on
forgejo_admin/notion-mcp-remote; downstream kustomize work onpal-e-deployments(#1044); namespace onpal-e-platform(#1045). Correctly scoped.Dependencies
Unchanged from prior review and still adequately documented: blocks on #1046 (OAuth integration) for real client id/secret, and on #1045 (tofu apply) for the namespace. Blocks downstream #1044 (kustomize overlay) and #1043 (ArgoCD Application) per SOP.
Acceptance Criteria
Strengthened materially. Previously testable-but-shallow (jsonpath key check + secret presence). Now forces the pod to actually come up with the bound values: rollout status, in-container
envverification, and/health200. Becauseserver.pyfails loudly on missing required env, a green pod proves the secret shape matches the manifest — this is exactly the right end-to-end assertion for this ticket.Blast Radius
Unchanged. First-time bootstrap — no token encryption collateral. No sibling services consume
notion-mcp-secrets. Prior reviewer's optional nit aboutSESSION_SECRETrotation impact was not added to the body — not a blocker for first-time provisioning, but worth a follow-up doc note if this ticket triggers future rotation runbooks.Decomposition Assessment
1 repo, 0 file targets, 7 AC items (still a single
kubectl create+ verification loop), <5 minutes of operator time. No decomposition needed.Recommendation
No action needed. Ticket is ready to advance from
backlogtotodo.Summary
All
[BODY]gaps from the prior review are closed. Secret name, key casing, wiring model, and env-var mapping now exactly matchk8s/deployment.yamlon main. The acceptance criteria have been hardened to verify actual pod startup rather than just secret existence, which is the correct bar for this ticket. Verdict: APPROVED. - [x] Secret name corrected to
-
Review: feat: add overlays/notion-mcp-remote/prod kustomize overlay
review-1044-2026-04-21Verdict: NEEDS_REFINEMENT
Board item #1044 on
board-notion-mcp-remote-- Forgejo issueforgejo_admin/pal-e-deployments#132. Scope is substantially correct but three gaps will trip the dev agent. Fixable in the issue body.Template Completeness
- [x] Type (Feature)
- [x] Lineage
- [x] Repo
- [x] User Story
- [x] Context
- [x] File Targets
- [x] Acceptance Criteria
- [x] Test Expectations
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x]
story:ops-deploy-gitopslabel -- GitOps Deploy via pal-e Platform - [x] story note verified --
story-notion-mcp-remote-ops-deploy-gitopslisted inproject-notion-mcp-remoteuser-stories section - [x]
arch:kustomize-overlaylabel -- refers to cross-service deployment pattern - [ ] arch note MISSING --
search_notes(query="arch-kustomize-overlay")returns empty.convention-kustomize-overlayexists and is the canonical reference, but noarch-prefixed note. [SCOPE] Decide whether to (a) createarch-kustomize-overlay(likely a cross-service arch diagram showing bases/standard -> overlay -> ArgoCD Image Updater write-back chain), or (b) relabel this ticket to droparch:kustomize-overlayand rely onconvention-kustomize-overlaysince kustomize-overlay is really a convention, not a diagrammable arch component of this service. Recommend (b) -- kustomize-overlay is a platform convention, and the service-specific arch is already captured inarch-deployment-notion-mcp-remote. - [x]
type:featurelabel matches### Type Featureheader - [x] Forgejo issue open (
forgejo_admin/pal-e-deployments#132)
File Targets
- [x]
overlays/notion-mcp-remote/prod/kustomization.yaml-- path matches existing sibling overlays (overlays/pal-e-docs/prod/,overlays/basketball-api/prod/, etc.) confirmed in pal-e-deployments repo - [x]
overlays/notion-mcp-remote/prod/deployment-patch.yaml-- standard overlay pattern perconvention-kustomize-overlay - [ ]
overlays/notion-mcp-remote/prod/ingress.yaml-- ISSUE: listed as "possibly" with the check "only if Tailscale Funnel ingress is not created by pal-e-services terraform". Confirmed viaarch-deployment-notion-mcp-remote("Tailscale Funnel provisioned by pal-e-services terraform whenfunnel=true") and pal-e-docs overlay (no ingress.yaml present). Funnel Ingress is NOT the overlay's concern. [BODY] Remove the "Possibly" line entirely and add "ingress.yaml -- NOT created here; Tailscale Funnel ingress is provisioned by pal-e-services terraform (funnel=true)" to the "Files NOT to touch" list. - [x] base source -- notion-mcp-remote
k8s/directory verified to contain deployment.yaml, service.yaml, pvc.yaml, servicemonitor.yaml, kustomization.yaml on main
Base Factoring Ambiguity (CRITICAL)
Issue body says: "references the base (either inline manifests from the service repo or a new
bases/notion-mcp-remote/if the pattern favours that)". This offers THREE options without choosing one. The convention is clear:convention-kustomize-overlayspecifies overlays consumebases/standard/(placeholdername: app) +bases/servicemonitor/with JSON6902 rename patches fromapp-> service name.- Confirmed by inspecting
overlays/pal-e-docs/prod/kustomization.yaml-- uses../../../bases/standardand../../../bases/servicemonitorplus rename JSON6902 patches. - The existing
notion-mcp-remote/k8s/base already usesname: notion-mcp-remote(notapp), so it CANNOT be consumed by the standard overlay pattern without modification. - The service also has a PVC and custom liveness/readiness probes on
/health(not/healthz), plus Recreate strategy and custom port, so it doesn't neatly fitbases/standardeither.
[BODY] Pick ONE approach and state it explicitly. Two viable choices:
- Reference the service repo's
k8s/as a remote base (e.g.resources: - https://forgejo.tail5b443a.ts.net/forgejo_admin/notion-mcp-remote.git//k8s?ref=main). Overlay patches tag viaimages:transformer and tweaks prod-specific bits. This matches the terraformsource_repo/source_pathrollback story inconvention-kustomize-overlay. Simpler for a service with non-standard shape (PVC, Recreate, /health probe). - Factor a new
bases/notion-mcp-remote/-shaped base with the service's resources and rename them in the overlay. More work, diverges frombases/standard, and puts two copies of the deployment spec in two repos (service repo k8s/ + deployment repo bases/).
Recommend option 1. Update the File Targets section to spell it out.
Secret Name + Shape Mismatch (CRITICAL)
Issue body says:
Real secrets (NOTION_OAUTH_CLIENT_ID, NOTION_OAUTH_CLIENT_SECRET, SESSION_SECRET, ONBOARD_SECRET, BASE_URL) are provisioned via
kubectl create secret generic... Overlay references the secret by name viaenvFrom. ...envFrom: secretRef: name: notion-mcp-remoteBut the existing base
notion-mcp-remote/k8s/deployment.yamlon main already defines each env var viavalueFrom: secretKeyRef: name: notion-mcp-secrets, key: {kebab-case}:- Secret name:
notion-mcp-secrets(NOTnotion-mcp-remote) - Shape: per-key
secretKeyRefwith kebab-case keys (oauth-client-id,oauth-client-secret,session-secret,onboard-secret,base-url) -- NOTenvFrom - Env var names uppercase SCREAMING_SNAKE (
NOTION_OAUTH_CLIENT_IDetc.) are injected via the per-key binding, not viaenvFrom(which would require matching uppercase keys in the secret)
So the overlay CANNOT just add
envFrom: secretRef: name: notion-mcp-remote. Either:- Align the overlay with the existing base (keep
secretKeyRefshape, keep namenotion-mcp-secrets), in which case the deployment-patch doesn't need to touch env/secrets at all and the overlay is just image tag + prod tweaks; or - Change the base deployment in
notion-mcp-remote/k8s/to useenvFromand uppercase keys, AND change the sibling secret-creation ticket (notion-mcp-remote #7,arch:k8s-secret) to match. This is a bigger scope change that affects two repos.
[BODY] Resolve the mismatch. Recommended: pick option 1 -- state that the overlay does NOT need to patch secrets because the base already wires them correctly; the overlay only needs to pin image tag, set replicas/resources, and carry Image Updater annotations. Update Acceptance Criteria bullets about
envFromand secret namenotion-mcp-remoteto match: the secret name to be provisioned (per sibling ticket) must benotion-mcp-secretswith kebab-case keys.Repo Placement
OK. Issue is filed on
forgejo_admin/pal-e-deploymentsand File Targets are alloverlays/notion-mcp-remote/prod/*. Correct repo. Service repo k8s manifests already exist (commits 455b62c, 837e2f2).Dependencies
- Hard prerequisite: Secret
notion-mcp-secretsmust exist in the target namespace BEFORE first ArgoCD sync (service-onboarding-sop step 3 warning). Tracked by notion-mcp-remote issue #7 (board item #1047,story:ops-deploy-gitops,arch:k8s-secret) -- NOT named in the issue body. [BODY] Name the sibling issue explicitly. - Downstream consumer: pal-e-services
var.servicesentry (board item #1043, Forgejopal-e-services#57) will setsource_path = "overlays/notion-mcp-remote/prod". Overlay path name is contractual with that ticket -- must not change after this merges. - Downstream consumer: pal-e-platform issue #296 (board item #1045,
arch:argocd) -- ArgoCD Application creation. Order: overlay lands -> secret provisioned -> tofu apply creates Application -> ArgoCD syncs. - Image tag: First-deploy bootstrap -- Image Updater writes
newTagto overlay after Woodpecker pushes. Per convention, must set a placeholdernewTag(issue sayslatest, but convention says full SHA format^[0-9a-f]{7,40}$). [BODY] ChangenewTag: latestto "setnewTagto the SHA from a successful Woodpecker build on main (Image Updater then rewrites). Do NOT uselatest-- violates the tag regex^[0-9a-f]{7,40}$that Image Updater expects."
Acceptance Criteria
Mostly testable, but three gaps:
- No AC covers the
images:transformer entries (per convention there should be two:app-image->harbor.../notion-mcp-remote/notion-mcp-remotewith SHA, plus the Image Updater write-back target). [BODY] Add: "images:transformer in kustomization.yaml has both entries (app-imagemapping and Harbor URL mapping) with matchingnewTagin full-SHA format." - "Container port 8000 matches Dockerfile EXPOSE" -- port 8000 is already set in the base deployment; unless overlay is overriding it, this AC is testing the base, not the overlay. [BODY] Either remove or rephrase as "Rendered Deployment keeps containerPort 8000 (no port override in overlay)."
- AC about
envFromand secret namenotion-mcp-remotewill be obsolete after the secret-shape refinement above. [BODY] Rewrite to match whichever approach is chosen.
Blast Radius
Low. Single overlay in pal-e-deployments; no change to
bases/standard, no change to other services' overlays. Overlay is inert until the pal-e-services ArgoCD Application points at it (#1043) AND the secret exists (#1047). Cannot break prod by merging this alone.Follow-on risk: if base factoring option 2 (new
bases/notion-mcp-remote/) is chosen, future changes to the service's deployment shape must be kept in sync across two repos -- avoid.Decomposition Assessment
3 points, 2-3 files, 1 repo, ~6 AC. Fits single agent pass (<5 min). No decomposition needed.
Recommendation
NEEDS_REFINEMENT. Apply these fixes to the Forgejo issue body:
[BODY]Resolve base factoring ambiguity: pick option 1 (reference service repo'sk8s/as a remote base via git URL) and state it explicitly. Remove the "inline manifests from the service repo or a new bases/notion-mcp-remote/" alternatives.[BODY]Fix secret name + shape mismatch. The existing base already usessecretKeyRefwith secret namenotion-mcp-secretsand kebab-case keys. The overlay should not patch env/secrets; the secret-provisioning sibling ticket (notion-mcp-remote#7) must createnotion-mcp-secretswith keysoauth-client-id, oauth-client-secret, session-secret, onboard-secret, base-url.[BODY]Name the sibling secret-provisioning ticket by URL in the Context/Dependencies section (notion-mcp-remote#7) so the dev agent can cross-check.[BODY]Remove the "Possiblyingress.yaml" line from File Targets. Add an explicit "NOT to touch" entry:ingress.yamlis provisioned by pal-e-services terraform (funnel=true).[BODY]ReplacenewTag: latestwith SHA-format bootstrap perconvention-kustomize-overlayImage Management section.[BODY]Update Acceptance Criteria to (a) drop theenvFrom/secret-name AC, (b) add an AC that theimages:transformer has both entries with full-SHAnewTag.[SCOPE]Decide: drop thearch:kustomize-overlaylabel on the board item (kustomize-overlay is a platform convention, not a service-specific arch diagram; the service's arch is covered byarch-deployment-notion-mcp-remote). If kept, createarch-kustomize-overlayas a cross-service platform-level arch note.
Non-blocking: the ticket is well-structured and the sequence is correct; refinements above bring it into alignment with the existing pal-e-deployments pattern and the already-merged service base so the dev agent doesn't have to make these calls itself.
-
Review: Provision k8s secrets for notion-mcp-remote
review-1047-2026-04-21Verdict: NEEDS_REFINEMENT
Board item #1047 / Forgejo issue forgejo_admin/notion-mcp-remote#7 — feat: provision app secrets in notion-mcp-remote namespace (pre-sync). Type: Feature. Points: 2. Labels:
type:feature,story:ops-deploy-gitops,arch:k8s-secret.Template Completeness
Checked against
template-issue-featureexpected sections:- [x] Type — Feature
- [x] Lineage — standalone, cites project + story + SOP row
- [x] Repo —
forgejo_admin/notion-mcp-remote - [x] User Story — role / want / so-that present
- [x] Context — references SOP, envFrom pattern, dependencies called out
- [x] File Targets — correctly marked "No repo file changes. This is a
kubectloperation." - [x] Acceptance Criteria — 5 items, each verifiable
- [x] Test Expectations — present, with runnable kubectl command
- [x] Constraints — three guardrails (no git-stored manifests, generated entropy, namespace prereq)
- [x] Checklist — 4 items
- [x] Related — links to project, story, SOP, arch note
Traceability
- [x]
story:ops-deploy-gitopslabel — GitOps Deploy via pal-e Platform - [x] story note verified —
story-notion-mcp-remote-ops-deploy-gitopsexists; listed inproject-notion-mcp-remoteuser-stories section; AC row "Secrets provisioned viakubectl create secret genericbefore first ArgoCD sync" is the exact line this ticket fulfills - [~]
arch:k8s-secretlabel — no dedicatedarch-k8s-secretnote exists. The relevant architecture coverage lives inarch-deployment-notion-mcp-remote(key decision: "Secrets created viakubectlbefore first sync") and the platform-widearch-secrets-pipeline. The label is unusual for this project (other tickets on this board use service-scoped arch labels likearch:notion-oauth,arch:tailscale-funnel). - [x] Forgejo issue — URL valid, issue #7 is open, body is well-formed
File Targets
No repo file targets (Feature ticket is a pure operator/kubectl action). However, to verify that the ticket's secret shape matches what the code actually consumes, I spot-checked the deployment manifest and server envvars.
- [x]
server.py— confirmed readsNOTION_OAUTH_CLIENT_ID,NOTION_OAUTH_CLIENT_SECRET,SESSION_SECRET,BASE_URL,ONBOARD_SECRET(lines 19–25). No Fernet/token-encryption key is read beyondSESSION_SECRETwhichTokenStoreuses for encryption. Env-var names in the ticket match the code. - [!]
k8s/deployment.yaml— MISMATCH. Currently references Secret namenotion-mcp-secrets(notnotion-mcp-remoteas the ticket AC specifies), viavalueFrom: secretKeyRef(notenvFrom), with keys in kebab-case:base-url,oauth-client-id,oauth-client-secret,session-secret,onboard-secret. If an operator follows the ticket AC literally (kubectl create secret generic notion-mcp-remotewith UPPER_SNAKE keys), the pod willCreateContainerConfigErrorbecause the secret the Deployment references does not exist and the keys won't line up.
This is the primary refinement blocker. See Recommendations.
Repo Placement
OK. Issue lives on
forgejo_admin/notion-mcp-remote; the kustomize overlay that would reference the secret lives onforgejo_admin/pal-e-deployments(tracked by board item #1044); the namespace lives onforgejo_admin/pal-e-platform(issue #296, board item #1045). Cross-repo linkage is correct — this ticket correctly scopes only thekubectlprovisioning step.Dependencies
- Blocks on issue #6 (board #1046) — "feat: register public Notion OAuth integration for remote proxy" — produces the real CLIENT_ID / CLIENT_SECRET values. Currently in
backlog. Dependency is explicitly documented in the Context section. OK. - Blocks on pal-e-platform issue #296 (board #1045) — "tofu apply" that creates the
notion-mcp-remotenamespace. Currently inbacklog. Dependency is explicitly documented in Constraints. OK. - Blocks pal-e-deployments #132 (board #1044) kustomize overlay and pal-e-services #57 (board #1043) ArgoCD Application — both must not sync until secrets exist, or they will be replaced with placeholders per SOP. Not explicitly documented as downstream blockees in the issue; the SOP reference implies it.
- All dependencies are documented adequately for an operator to sequence the work.
Acceptance Criteria
Criteria are testable and each has a runnable verification command. The final AC "Disaster-recovery copy stored in
~/secrets/notion-mcp-remote/" aligns withsop-secrets-managementLayer 2 (plaintext.envhost backup). The issue picks the legacy manual kubectl path (documented insop-secrets-management→ "Adding a new app secret (manual path — legacy)"), which is still explicitly blessed by thepre-deploy-validationchecklist row for "Application secrets."The SOP does note that SOPS+Age is the preferred path for new services. The ticket chose manual kubectl, which is a valid strategic choice for a pre-sync bootstrap (SOPS still requires ArgoCD to run decryption, so manual is the only option strictly before the first sync). No refinement needed on this front — calling it out for visibility.
Blast Radius
- Other remote MCP services on the platform (e.g. gmail-mcp-remote pattern) likely use the same pre-sync manual path. No collateral risk.
- Regenerating
SESSION_SECRETinvalidates any existing encryptedtokens.json— since this is the first-time bootstrap (no prior tokens), no data loss. Worth a callout in the issue body for future rotations. - If the Secret name / keys are fixed (see refinement below), no code change is needed; if
envFromis adopted instead, the Deployment manifest changes and a new image need not be rebuilt but the Deployment patch will churn.
Decomposition Assessment
1 repo, 0 file targets, 5 AC items, single kubectl command plus backup copy. Estimated operator time once dependencies are met: <5 minutes. No decomposition needed.
Recommendation
[BODY]Reconcile the secret name and key shape withk8s/deployment.yaml. The deployment currently references Secretnotion-mcp-secretswith kebab-case keys viavalueFrom: secretKeyRef. Pick one of two paths and update the AC accordingly:- Option A (preferred, matches current manifest): change AC to
kubectl create secret generic notion-mcp-secretswith keysbase-url,oauth-client-id,oauth-client-secret,session-secret,onboard-secret. Update the jsonpath AC accordingly. Zero code / manifest churn. - Option B: keep the ticket's
notion-mcp-remote/ UPPER_SNAKE /envFromshape, and add a file target: patchk8s/deployment.yamlto useenvFrom: - secretRef: name: notion-mcp-remote. This aligns with the Context paragraph ("envFrom") but is a bigger change and would overlap scope with board item #1044 (kustomize overlay).
- Option A (preferred, matches current manifest): change AC to
[LABEL]Consider replacingarch:k8s-secretwith a more specific, existing arch note reference.arch:deployment-notion-mcp-remotewould map to the existing arch note that already documents the "secrets before first sync" decision;arch:secrets-pipelinewould map to the platform-wide note. Optional — the current label is interpretable but inconsistent with the rest of this board.[BODY](nit) The Context paragraph referencesenvFromas the reference mechanism, which contradicts both the current deployment (valueFrom) and Option A above. Update whichever language survives the refinement.[BODY](nit) Consider adding a post-provision callout to the issue body: "IfSESSION_SECRETis ever rotated, any persisted encrypted tokens in the PVC become unreadable and users must re-OAuth." This is first-time bootstrap so it is not a blocker, but future readers benefit.
Summary
Scope, traceability, dependencies, and SOP alignment are all solid. One concrete, fixable mismatch between the AC's secret shape and what the deployment manifest actually reads is the reason this ticket is NEEDS_REFINEMENT rather than READY. Once the AC is reconciled with
k8s/deployment.yaml(Option A is the least churn), the ticket can proceed totodo. -
Review: var.services entry for notion-mcp-remote (ArgoCD discovery)
review-1043-2026-04-21Verdict: NEEDS_REFINEMENT
Board item: #1043 on
board-notion-mcp-remote-- Forgejo issueforgejo_admin/pal-e-services#57. Type: Feature. Points: 3. Labels:type:feature,story:ops-deploy-gitops,arch:argocd.Template Completeness
- [x] Type (Feature)
- [x] Lineage (standalone, scoped from project-notion-mcp-remote)
- [x] Repo (forgejo_admin/pal-e-services)
- [x] User Story
- [x] Context (detailed, includes key values)
- [x] File Targets
- [x] Acceptance Criteria
- [x] Test Expectations (with run command)
- [x] Constraints
- [x] Checklist
- [x] Related
Traceability
- [x]
story:ops-deploy-gitopslabel -- maps tostory-notion-mcp-remote-ops-deploy-gitops - [x] story note verified -- listed in
project-notion-mcp-remoteuser-stories table (AC: "pal-e-services var.services entry landed via PR") - [x]
arch:argocdlabel -- component: ArgoCD (entry drives ArgoCD Application discovery) - [ ] arch note MISSING -- [SCOPE] No
arch-argocdnote exists in pal-e-docs.search_notes(query="arch-argocd")returned zero results. Createarch-argocddescribing ArgoCD's role as the GitOps sync engine for pal-e services (driven byargocd_application.serviceinterraform/services.tf). - [x] Forgejo issue --
forgejo_admin/pal-e-services#57, open
File Targets
- [x]
terraform/k3s.tfvars-- verified path. File is gitignored (per repo.gitignore:*.tfvarsexcluded, only*.tfvars.exampletracked). This is expected:sop-platform-tf-changesdocuments pal-e-services as "plan-and-apply-before-merge" withk3s.tfvarsedited locally. Agent must have localk3s.tfvarsavailable. - [ ]
terraform/services.tf-- [BODY] imprecise. Issue says "Possiblyterraform/services.tf-- if the module signature needs adjustment to passfunnel=true". This is not needed.services.tfalready handles per-service funnel viakubernetes_ingress_v1.service_funnelwithfor_each = { for k, v in var.services : k => v if v.funnel }, andvariables.tfalready exposesfunnel = boolin thevar.servicesobject type. Remove the speculative "possibly services.tf" bullet -- it's already supported. A dev agent could burn time "inspecting" a file that needs no change.
Repo Placement
Correct. Issue filed on
forgejo_admin/pal-e-serviceswhereterraform/k3s.tfvarsand thevar.servicesschema live. Single-repo change; no multi-repo coordination within this ticket.Dependencies
- Upstream (blocking):
forgejo_admin/pal-e-deployments#132-- kustomize overlay atoverlays/notion-mcp-remote/prod. Currently OPEN, board item #1044 inbacklog. Ticket correctly documents this ("Sibling overlay PR on pal-e-deployments must land first"). ArgoCD sync will fail if the overlay path doesn't exist in thepal-e-deploymentsrepo on the branch referenced bytarget_revision = main. - Downstream:
forgejo_admin/pal-e-platform#296(tofu apply to provision) -- board item #1045 inbacklog. Ticket does not name this explicitly, but it is implied by "PR reviewed and merged" -- the actualtofu applyagainst infrastructure is a separate board item handled after this PR lands. [BODY] optional: add an explicit "Downstream: pal-e-platform#296 handles the tofu apply" bullet to Related for dev-agent clarity, since this ticket's AC includes "tofu plan" but not "tofu apply" (the SOP requires manual apply-before-merge on pal-e-services).
Schema precision issues (BODY fixes)
- [BODY] source_repo value is underspecified. Issue body says
source_repo: pal-e-deployments. Verified againstvariables.tf(variable "services"schema) andservices.tf(coalesce(each.value.source_repo, each.value.forgejo_repo)interpolated ashttp://forgejo-http.forgejo.svc.cluster.local/${source_repo}.git), plus thek3s.tfvars.exampleexample which usesforgejo_admin/pal-e-deploymentsinforgejo_repo. The value MUST include theforgejo_admin/owner prefix:source_repo = "forgejo_admin/pal-e-deployments". Without the prefix, ArgoCD will attempt to sync from an invalid URLforgejo-http.forgejo.svc.cluster.local/pal-e-deployments.gitand fail. Fix in issue body. - [BODY] Clarify workflow expectation. Per
sop-platform-tf-changes, pal-e-services uses plan-and-apply-before-merge (manualtofu apply), not CI-driven apply-on-merge (which is the pal-e-platform pattern). The ticket's AC "tofu plan ... no unexpected diff" + "PR reviewed and merged" is correct, but the agent needs to know the apply is manual and happens BEFORE PR merge. Add a sentence to Constraints: "pal-e-services uses plan-and-apply-before-merge per sop-platform-tf-changes -- do NOT rely on CI apply; a human operator runstofu apply -lock=false -var-file=k3s.tfvarsafter plan review."
Acceptance Criteria
Four ACs, all verifiable by an agent:
- AC1 (entry present) -- grep-verifiable.
- AC2 (plan diff shows expected resources) -- runnable; the expected resource list is explicit (ArgoCD Application, namespace, Harbor project + robots, image pull secret, Tailscale Funnel ingress). Matches
services.tfresources exactly. - AC3 (values match pre-deploy-validation checklist rows) -- references
service-onboarding-sop(slug not found viasearch_notes; may live under a different slug but is referenced consistently across platform docs -- treat as present). - AC4 (PR reviewed and merged) -- process criterion, verifiable via PR URL.
Blast Radius
Additive-only change to a single map key in
var.services. Theservices.tfresources are allfor_each = var.services, so no existing service's resources are touched. Harbor project names are unique (notion-mcp-remotehas not been taken). Namespace collision risk: low (namespacenotion-mcp-remoteshould not exist yet). The only subtle risk is: if any other service in the currentk3s.tfvarshas drifted from its declared state,tofu planmay show unexpected diffs beyond notion-mcp-remote -- the agent must verify the diff is isolated to new resources only. The recentreview-1064-2026-04-20(tf state drift) incident on pal-e-services makes this a concrete concern; flag to agent.Decomposition Assessment
1 file edit (k3s.tfvars, one map entry), 4 acceptance criteria, estimated agent work ~5-10 minutes including plan review. No decomposition needed. 3 points is reasonable given the plan-review + manual-apply coordination overhead specific to pal-e-services.
Recommendation
[BODY]Fixsource_repovalue:pal-e-deployments->forgejo_admin/pal-e-deployments(full owner/repo form; required byservices.tfURL construction).[BODY]Remove the "Possiblyterraform/services.tf" speculation under File Targets. The module already supports per-servicefunnel=true; no module adjustment is needed. Replace with explicit "File Targets: terraform/k3s.tfvars only" to prevent the agent from opening/editingservices.tf.[BODY]Add workflow clarification to Constraints: pal-e-services uses plan-and-apply-before-merge (manualtofu apply), not CI-driven apply-on-merge. Referencesop-platform-tf-changes.[BODY]Add a "Blast Radius" note: verifytofu plandiff is isolated to new notion-mcp-remote resources only; if other services' resources show drift, stop and escalate (referencereview-1064-2026-04-20).[SCOPE]Create architecture notearch-argocddescribing ArgoCD's role in the platform (GitOps sync engine,argocd_application.serviceper-service via pal-e-services Terraform, Image Updater write-back pattern). Without this note, thearch:argocdlabel points to a missing document -- same pattern as the missingarch-tailscale-funnelgap flagged inreview-788-2026-04-03.
Once refinements land
Re-review expected to verdict READY. The scope is otherwise sound, file path correct, dependencies mapped, ACs verifiable, blast radius understood.
-
Review: Activate Woodpecker pipeline for notion-mcp-remote
review-1048-2026-04-21Verdict: APPROVED
Scope is solid. Feature template fully satisfied, traceability triangle complete (minus the non-blocking
arch-woodpeckernote — same gap flagged in review-273 and review-882 and treated as non-blocking per precedent). Dependency on issue #5 (Harbor URL fix) is explicitly captured. File-target absence is correct for this ticket type — it is a Woodpecker-admin ticket, not a code change.One notable field-state observation: the repo already reports
active: truein Woodpecker (id 16). AC1 may already be satisfied mechanically. The implementing agent must verify activation is functional (webhook actually fires on a test commit) rather than just read the flag — see advisory below.Template Completeness
- [x] Type — Feature
- [x] Lineage — standalone, scoped from project-notion-mcp-remote, cites service-onboarding-sop steps 7–8
- [x] Repo —
forgejo_admin/notion-mcp-remote - [x] User Story — operator wants activation + Harbor credentials so
mainpush triggers build-and-push - [x] Context — explains why activation is manual (woodpecker-sdk #6 gap), why MCP tool is required for secrets
- [x] File Targets — explicitly "No repo file changes. Woodpecker UI + MCP tool calls only." Correct for this ticket type.
- [x] Acceptance Criteria — 5 concrete, machine-verifiable conditions
- [x] Test Expectations —
mcp__woodpecker__list_pipelinesand build step log inspection - [x] Constraints — waits on tofu apply, requires MCP not UI
- [x] Checklist — 6 steps covering prereqs and verification
- [x] Related — project, story, SOP
Traceability
- [x] story:ops-deploy-gitops label — matches
story-notion-mcp-remote-ops-deploy-gitops - [x] story note verified — listed in
project-notion-mcp-remoteuser-stories table - [x] arch:woodpecker label — identifies the Woodpecker CI component
- [ ] arch note MISSING —
arch-woodpeckersearch returned zero results. Non-blocking per precedent (review-273, review-882 flagged the same gap as non-blocking). See[SCOPE]recommendation below. - [x] Forgejo issue — forgejo_admin/notion-mcp-remote#8, state: open
File Targets
Issue states: "No repo file changes. Woodpecker UI + MCP tool calls only." This is correct — activation and secret provisioning are out-of-band platform operations, not repo edits. No file-target verification needed. Skipped per skill-review-ticket guidance for tickets with no file targets (Task-style scope inside a Feature wrapper).
Repo Placement
Issue is filed on
forgejo_admin/notion-mcp-remote, the repo being activated. Correct placement. No cross-repo file changes required. The prerequisite Harbor robot lives inpal-e-platform(tracked separately inpal-e-platform#296/pal-e-services#57) — the issue correctly treats those as upstream dependencies rather than pulling them into scope.Dependencies
- Hard prereq: Issue #5 (Harbor URL fix) must merge before AC5 can pass. Acknowledged in the Checklist. AC4 is satisfiable without it — a feature-branch push will reach the build-and-push step and fail at registry URL, which still proves activation + secrets are wired.
- Soft prereq:
tofu applyon pal-e-platform must produce the Harbor robot credentials before AC3 (values match robot, not admin). Tracked in board items #1045 (pal-e-platform#296) and #1043 (pal-e-services#57). Acknowledged in Constraints and Checklist. - Board state: All three dependencies (#5, pal-e-platform#296, pal-e-services#57) are in
backlog. None inin_progress. Ordering is captured correctly in the ticket Checklist but no item is yet blocking this one on the board layer — the implementer must respect the Checklist ordering.
Acceptance Criteria
All 5 AC are concrete and machine-verifiable:
- AC1 "Repo visible as active in Woodpecker UI" — verifiable via
mcp__woodpecker__get_repo(active: true). - AC2 "Secrets
harbor_usernameandharbor_passwordvisible" — verifiable viamcp__woodpecker__list_repo_secrets. - AC3 "Values match Harbor robot (not admin)" — verifiable by cross-referencing Harbor robot account from tofu output.
- AC4 "Feature branch push triggers pipeline that reaches build-and-push step" — verifiable via
mcp__woodpecker__list_pipelines. Proves activation + secrets without needing the Harbor URL fix to be in place. - AC5 "Main push produces full green pipeline once Harbor URL fix lands" — gated on #5 merging; explicitly deferred.
AC4 is the critical validation step — it decouples activation verification from the Harbor URL fix dependency. Well-designed.
Blast Radius
Low. Pattern used by 7+ onboarded services (basketball-api, forgejo-mcp, gcal-mcp, gmail-mcp, pal-e-api, notion-mcp, westside-app, etc. — all visible as
active: truein Woodpecker). No novel work. Failure mode is limited to this one repo: a bad secret prevents pipeline from pushing, which is non-destructive and trivially reversible viamcp__woodpecker__update_repo_secret.Sibling-service scan: no other MCP-remote services in this fleet are missing activation. No pattern to spread.
Decomposition Assessment
File targets: 0 (platform ops only). AC count: 5 (within limit). Estimated agent time: 2–5 minutes (one activation check, two
create_repo_secretcalls, one test-commit push, one pipeline-list check). No subtask independence — linear sequential work.No decomposition needed.
Field-State Observations (for the implementer)
- Repo already reports
active: truein Woodpecker (id 16). This means the activation button was already pressed at some point. However,list_pipelinesfor this repo returns[]— no run has ever executed. Possible causes: (a) no push has occurred since activation (recent activation), (b) webhook is misconfigured and never fires (seetodo-woodpecker-webhook-token-fix— a known class of failure post-Postgres migration). The implementer MUST verify activation is functional by pushing a test commit and confirming a pipeline row appears, not just trust theactiveflag. list_repo_secretsreturns[]— harbor_username and harbor_password are genuinely missing. AC2 is real, outstanding work.- Dockerfile.k8s exists (referenced by
.woodpecker.yaml:25). Pipeline config is ready to run; the only block is secrets + the Harbor URL fix. .woodpecker.yamlis at.woodpecker.yaml(not.woodpecker.yml) — confirmed present on main at commit 837e2f2.
Recommendation
APPROVED. Ready to advance from backlog to todo, with the understanding that the implementer respects the Checklist ordering (Harbor robot from tofu must exist before AC3 can pass).
Advisory (non-blocking):
[SCOPE]Create architecture notearch-woodpeckerin pal-e-docs. This is a cross-project gap surfaced by multiple recent reviews (review-273, review-882) and not unique to this ticket. Owner is the pal-e-platform project, not this one. Do not block this ticket on it.[BODY]AC1 is mechanically already satisfied (active: truein Woodpecker). Suggest tightening AC1 to "Repo is active in Woodpecker and a test push produces a visible pipeline row inmcp__woodpecker__list_pipelines" so the implementer does not claim completion on a stale flag. Non-blocking — AC4 already covers the functional check, so an alert implementer will catch this anyway.
-
Review: E2E verify + claude.ai integration for notion-mcp-remote
review-1049-2026-04-21Verdict: NEEDS_REFINEMENT
Note: Minor scope gap — one missing backing note for the
arch:tailscale-funnellabel. Everything else is in order. The ticket itself is well-scoped as a verification-only validation pass for the claude-ai-connect user story.Template Completeness
Issue type: Feature (Type header = "Feature"). Routed against
template-issue-feature.- [x] Type — "Feature"
- [x] Lineage — standalone E2E verification ticket, proves Milestone M1
- [x] Repo —
forgejo_admin/notion-mcp-remote - [x] User Story — inline, matches
story-notion-mcp-remote-claude-ai-connect - [x] Context — explains dependency chain + references
sop-validation - [x] File Targets — explicitly "No repo file changes; verification + validation-note creation only" (appropriate for this ticket type)
- [x] Acceptance Criteria — 11 checks across Platform / E2E / Recordkeeping
- [x] Test Expectations — present with concrete pass conditions
- [x] Constraints — present, including "do not begin until all upstream tickets closed"
- [x] Checklist — present
- [x] Related — links to project, user story, ops-deploy-gitops story, sop-validation
Traceability
- [x]
story:claude-ai-connectlabel — story "Connect Notion to claude.ai" - [x] story note verified —
story-notion-mcp-remote-claude-ai-connectexists and is listed in theproject-notion-mcp-remoteuser-stories section - [x]
arch:tailscale-funnellabel present on board item - [ ] arch note MISSING — no
arch-tailscale-funnelnote exists in pal-e-docs. Tailscale Funnel behavior is covered piecewise inarch-deployment-notion-mcp-remote,arch-deployment-gdocs-daily-mcp-remote,arch-deployment-westside-mcp, andhost-inventory-archbox, but there is no shared architecture note backing thearch:tailscale-funnellabel. This is a recurring gap — the same finding was raised onreview-788-2026-04-03(playme2k funnel + kustomize). [SCOPE] Create sharedarch-tailscale-funnelnote documenting the Funnel proxy pattern (proxy pod intailscalenamespace,*.tail5b443a.ts.nethostnames, TLS termination, ingress handoff) so all services using that label link to one canonical diagram. - [x]
type:featurelabel — appropriate (new capability, not a bug fix) - [x] Forgejo issue — forgejo_admin/notion-mcp-remote#9 — open, body matches template
File Targets
Not applicable — ticket is verification-only. Deliverables are a validation note (per
sop-validation) and a milestone mark on the project page Status section. This is the correct shape for the claude-ai-connect validation ticket.Repo Placement
OK. The Forgejo issue is filed on
forgejo_admin/notion-mcp-remote, which is the correct owning service for the E2E validation. The validation note will live in pal-e-docs, and the milestone mark lands onproject-notion-mcp-remote— both are separate surfaces, not file changes in the repo.Dependencies
Seven prerequisite tickets must close before this ticket executes. All are currently open / in backlog:
- pal-e-platform#296 — tofu apply to provision notion-mcp-remote (open)
- pal-e-services#57 — add notion-mcp-remote to var.services (open)
- pal-e-deployments#132 — add overlays/notion-mcp-remote/prod kustomize overlay (open)
- notion-mcp-remote#5 — switch Woodpecker image push to internal Harbor URL (open)
- notion-mcp-remote#6 — register public Notion OAuth integration for remote proxy (open)
- notion-mcp-remote#7 — provision app secrets in notion-mcp-remote namespace (open)
- notion-mcp-remote#8 — activate repo in Woodpecker + add Harbor push secrets (open)
Dependencies are documented correctly in the Constraints section ("Do not begin this ticket until all upstream tickets closed"). The review->todo gate on backlog is fine for now; execution must not start until upstream work closes. No refinement needed on dependency documentation itself.
Acceptance Criteria
AC are testable with concrete commands and tool calls:
- Platform checks (6): Woodpecker pipeline status, ArgoCD sync+health, pod Running,
/health200,/.well-known/oauth-authorization-serverJSON, Prometheus scrape. All verifiable viamcp__woodpecker__list_pipelines,kubectl,curl, and Prometheus UI. Thenotion_mcp_remote_upmetric referenced in AC is confirmed present inserver.py. - E2E checks (4): claude.ai accepts URL, Notion OAuth consent < 60s, search tool returns real results, second tool call (get_page) succeeds. Matches all 6 AC of the user story.
- Recordkeeping (3): validation note per
sop-validation, Milestone M1 marked on project page Status, all prior tickets closed.
One small tightening opportunity — [BODY] The AC currently say "
curl https://notion-mcp-remote.tail5b443a.ts.net/.well-known/oauth-authorization-serverreturns valid JSON with the expectedissuer,authorization_endpoint,token_endpoint." Consider also asserting theregistration_endpointis present since Dynamic Client Registration is how claude.ai onboards. Minor; not a blocker.Blast Radius
Small. Ticket is observational — no config changes, no code merges. Risk is only that a failing check here exposes a real defect in upstream tickets (which is the point). If any check fails, the validation verdict is PARTIAL/FAIL and the appropriate upstream ticket gets reopened, not this one.
Decomposition Assessment
AC count is 11, which exceeds the default 5-AC threshold — but this is the explicit, intended shape of a validation ticket: one pass that exercises every AC of the user story, per
sop-validation. Estimated time is 15–20 minutes of browser + kubectl + curl work, not 5, but decomposing a validation ticket defeats its purpose (it would split cross-cutting concerns back into per-layer checks that already belong on the upstream tickets). Points=2 reflects this correctly.No decomposition recommended.
Recommendation
Verdict: NEEDS_REFINEMENT — one concrete gap to close before this moves to todo:
[SCOPE]Create shared architecture notearch-tailscale-funneldocumenting the Funnel proxy pattern used by this and other MCP services. This unblocks not just #1049 but every current/future ticket labeledarch:tailscale-funnel. Can reuse content already inhost-inventory-archbox,doc-network-traffic-map, andarch-deployment-notion-mcp-remote.[BODY](optional, nice-to-have) Addregistration_endpointto the list of OAuth metadata fields asserted in the Platform checks AC, since DCR is on the handshake hot path.
Upstream dependencies (7 open tickets) are correctly documented; no action on those from this review — they will be closed by their own tracks.
Related
- Board item 1049 —
board-notion-mcp-remotecolumn=backlog - Forgejo:
forgejo_admin/notion-mcp-remote#9 story-notion-mcp-remote-claude-ai-connectproject-notion-mcp-remotesop-validation- Prior funnel-arch-missing finding:
review-788-2026-04-03
-
Review: tofu apply to provision notion-mcp-remote
review-1045-2026-04-21Verdict: NEEDS_REFINEMENT
Single gap: the
arch:argocdlabel has no backing architecture note in pal-e-docs. The ticket's actual architectural anchor isarch-deployment-notion-mcp-remote, which does exist. Everything else is solid and apply-ready pending the two listed dependencies.Template Completeness
- [x] Type (Feature)
- [x] Lineage (references project-notion-mcp-remote, service-onboarding-sop step 5)
- [x] Repo (forgejo_admin/pal-e-platform)
- [x] User Story
- [x] Context (resources provisioned, lock-false + no -target rationale)
- [x] File Targets ("no file changes in this repo" — apply operation; follow-up-ticket guidance included)
- [x] Acceptance Criteria
- [x] Test Expectations (tofu fmt, validate, plan command)
- [x] Constraints (pre-deploy checklist, secrets pre-provisioned, lock-false, no -target)
- [x] Checklist
- [x] Related
Traceability
- [x] story:ops-deploy-gitops label — "GitOps Deploy via pal-e Platform"
- [x] story note verified — story-notion-mcp-remote-ops-deploy-gitops listed in project-notion-mcp-remote user-stories section
- [x] arch:argocd label present on board item
- [ ] arch note MISSING — no
arch-argocdnote exists in pal-e-docs. The ticket body referencesarch-deployment-notion-mcp-remote(which does exist and is the correct architectural anchor for this apply operation) - [x] Forgejo issue — https://forgejo.tail5b443a.ts.net/forgejo_admin/pal-e-platform/issues/296 open and valid
File Targets
Issue explicitly states no file changes in pal-e-platform — this is the apply operation itself plus recordkeeping. "Files NOT to touch" (network-policies.tf) is correctly called out. Follow-up-ticket guidance for any plumbing gaps surfaced by
tofu planis appropriate. No file-target verification applicable.Repo Placement
OK. Apply runs against
forgejo_admin/pal-e-platformterraform. Sibling tickets correctly scoped to pal-e-services (#57 var.services entry) and pal-e-deployments (#132 overlay). Application secret provisioning tracked separately on the notion-mcp-remote board. No repo-boundary confusion.Dependencies
Explicitly documented and present on board-notion-mcp-remote:
- pal-e-services #57 (var.services entry) — board item 1043, backlog, 3 pts — MUST LAND FIRST
- pal-e-deployments #132 (kustomize overlay) — board item 1044, backlog, 3 pts — MUST LAND FIRST
- notion-mcp-remote #7 (secrets kubectl create secret generic) — board item 1047, backlog, 2 pts — MUST LAND BEFORE FIRST ARGOCD SYNC (sibling, not blocker of tofu apply itself, but called out in Constraints to avoid ArgoCD clobbering)
Dependency chain is complete and correctly sequenced.
Acceptance Criteria
Verifiable and concrete:
tofu plan -lock=falsediff inspection (expected resources enumerated)- No unexpected destructive changes
- Lucas approval gate (explicit)
kubectl get ns notion-mcp-remote- ArgoCD Application Healthy + Synced
- Funnel URL (
notion-mcp-remote.tail5b443a.ts.net) resolves and returns a pod response
Each criterion is agent-checkable post-apply. Test Expectations section provides the exact plan command:
cd terraform && tofu plan -lock=false -var-file=k3s.tfvars.Blast Radius
Contained. First-deploy apply for a new service with no shared infrastructure modifications:
- Creates namespace, ArgoCD Application, Harbor project + robots, image pull secret, Tailscale Funnel ingress — all scoped to this service
- No NetworkPolicy changes (service only egresses to api.notion.com)
- No Keycloak client (Notion OAuth end-to-end)
- Risk: a terraform refactor since last apply could surface unrelated diff — the AC "no unexpected destructive changes on unrelated services" covers this and Lucas-gated plan review is the mitigation
- Aligns with
sop-platform-tf-changes(lock-false, Lucas approval, no -target) andservice-onboarding-sopstep 5 (pre-deploy-validation checklist must be 100% green first)
Decomposition Assessment
No decomposition needed. Single apply operation with one pre-check (plan review) and one gate (Lucas approval). File count: 0. AC count: 7 criteria but all verifiable in a single post-apply pass. Estimated agent work: ~3 min for plan + validation + apply + smoke check. Fits 5-minute rule.
Recommendation
One issue to resolve before this advances out of review:
[LABEL]OR[SCOPE]— Thearch:argocdlabel has no backingarch-argocdnote. Two options:- Preferred (LABEL fix): Replace
arch:argocdwitharch:deployment-notion-mcp-remoteon the board item so the label points at the real backing note (arch-deployment-notion-mcp-remote) that the ticket body already references. This matches the sibling tickets' spirit (arch:kustomize-overlay, arch:woodpecker are component-specific) but acknowledges this ticket's anchor is the service-level deployment diagram. - Alternative (SCOPE fix): Create a shared
arch-argocdnote describing ArgoCD as a platform component (Application resource lifecycle, Image Updater integration, sync modes) — reusable by pal-e-services #57 which uses the same label.
- Preferred (LABEL fix): Replace
All other scope elements (template, dependencies, AC, guardrails, blast radius) are solid. After the label/arch-note decision, this ticket is apply-ready once #57 and #132 merge and the pre-deploy validation checklist is green.
-
Review: Register Notion OAuth app
review-1046-2026-04-21Verdict: NEEDS_REFINEMENT
Ticket: board item #1046 —
forgejo_admin/notion-mcp-remote#6— feat: register public Notion OAuth integration for remote proxyLabels:
type:feature,story:claude-ai-connect,arch:notion-oauth— points: 1 — column: backlogTemplate Completeness
Validated against
template-issue-feature:- [x]
### Type— Feature - [x]
### Lineage— standalone, scoped from project - [x]
### Repo—forgejo_admin/notion-mcp-remote - [x]
### User Story— present (AS/I want/So that) - [x]
### Context— clear, explains three-legged flow and Public-vs-Internal distinction - [x]
### File Targets— present (explicitly "no repo file changes") and lists target secrets plus files NOT to touch - [x]
### Acceptance Criteria— 5 ACs, testable - [x]
### Test Expectations— manual browser smoke + curl smoke - [x]
### Constraints— three real constraints listed - [x]
### Checklist— present - [x]
### Related— project + story + arch
Traceability
- [x]
story:claude-ai-connectlabel — Connect Notion to claude.ai - [x] story note verified — entry exists in
project-notion-mcp-remoteuser-stories section, linking tostory-notion-mcp-remote-claude-ai-connect - [x]
arch:notion-oauthlabel — OAuth handshake component - [ ] arch note MISSING under that exact slug —
search_notes("arch-notion-oauth")returned no results. The OAuth handshake is documented, but underarch-dataflow-notion-mcp-remote(sequenceDiagram explicitly covering authorize/callback/token).[SCOPE]Either rename the label toarch:dataflow-notion-mcp-remoteor create a dedicatedarch-notion-oauthcomponent note. Recommend re-labeling since the dataflow note already covers this material. - [x] Forgejo issue —
https://forgejo.tail5b443a.ts.net/forgejo_admin/notion-mcp-remote/issues/6, open, created 2026-04-21
File Targets
This is credential provisioning — no repo file edits expected. The ticket correctly states this and lists downstream consumers:
- [x]
~/secrets/notion-mcp-remote/credentials.env— verified directory exists at~/secrets/notion-mcp-remote/ - [x]
server.py:19-20already readsNOTION_OAUTH_CLIENT_ID/NOTION_OAUTH_CLIENT_SECRETfrom env — verified via Grep - [x]
.env.example:2-3uses the same two key names — verified - [x]
k8s/deployment.yaml:43-52mounts both from secretnotion-mcp-secretskeysoauth-client-id/oauth-client-secret— verified - [x] "Do not touch" list is accurate —
server.pyandrequirements.txtcorrectly already consume these env vars
Repo Placement
OK — the issue is filed on
forgejo_admin/notion-mcp-remoteand the credentials feed this service's secret. Sibling ticket #7 (k8s secret provisioning) and #8 (Woodpecker activation) are correctly split into separate issues because they touch different systems.Dependencies
Board scan (
board-notion-mcp-remote):- This ticket blocks:
- #7 (issue 1047,
arch:k8s-secret) — provisions app secrets innotion-mcp-remotenamespace, needs CLIENT_ID/SECRET values - #9 (issue 1049,
arch:tailscale-funnel) — E2E verify / claude.ai connect, needs the integration to exist
- #7 (issue 1047,
- Not blocked by anything in in_progress — safe to pull.
- Dependencies implied in context ("Blocks ticket #5 / #8") per router brief do not match the actual board — ticket #5 is the Harbor/Woodpecker CI bug, and #8 is Woodpecker repo activation, neither of which consume Notion OAuth credentials directly.
[BODY]Clarify blocker chain in the issue body: this ticket unblocks #7 (secret provisioning) and #9 (E2E verify), not #5 and #8. (Or correct the router brief — this is a router-context issue, not a ticket-body issue, so noting for Ava.)
Acceptance Criteria
All 5 ACs are verifiable by an agent/human:
- Integration exists in Daily workspace — verifiable by loading the Notion integrations UI.
- Distribution is Public — same UI verification.
- Redirect URI exact — ISSUE: wrong path. The ticket specifies
https://notion-mcp-remote.tail5b443a.ts.net/callback, but the running service advertises and consumes/oauth/callback(confirmed inmcp-remote-auth/src/mcp_remote_auth/routes.py:41andprovider.py:125,151). Using/callbackwill cause Notion to reject the token exchange.[BODY]Fix redirect URI tohttps://notion-mcp-remote.tail5b443a.ts.net/oauth/callbackin the AC, the smoke curl, and the Constraints section. - Creds captured in
~/secrets/notion-mcp-remote/credentials.env— testable by file presence + key names. - Smoke curl returns consent HTML — valid once redirect URI is fixed.
[BODY]Optional consideration: the service also exposes/onboard/callback(onboarding flow,onboarding.py:136). Out of scope for the claude-ai-connect story, but if Lucas plans to use the same public integration for onboarding, that redirect URI should also be registered now to avoid a second manual trip to the Notion UI.Blast Radius
- Credential leakage: Constraint already calls out "do not commit CLIENT_SECRET to any git repo." Good.
- Redirect URI mismatch: If the wrong path (
/callback) is configured with Notion, the entire OAuth leg fails silently at the token exchange. The ACs as written would be marked green by an agent but the service would still be broken end-to-end — #9 (E2E verify) would catch it, but a lot of work happens in between. Fixing AC #3 prevents this. - Base URL drift:
BASE_URLenv var is the single source of truth; if the Tailscale Funnel hostname ever changes, the Notion integration redirect URI must be updated in the Notion UI. Worth a note in Constraints.
Decomposition Assessment
1 point, 5 ACs, no code edits, one UI flow. Well within the 5-minute rule. No decomposition needed.
Recommendation
Two concrete fixes required before this can move to
todo:[BODY]Fix the redirect URI path throughout the issue (AC #3, smoke curl URL in AC #5, Constraints, Test Expectations):/callback→/oauth/callback. This is the critical fix — the current path will silently break the handshake.[SCOPE]Resolve thearch:notion-oauthlabel: either (a) change the label toarch:dataflow-notion-mcp-remoteto match the existing architecture note, or (b) create a dedicatedarch-notion-oauthcomponent note. Option (a) is lighter weight and the existing note already captures the handshake.
Optional (non-blocking):
[BODY]Add/onboard/callbackas a second registered redirect URI if the same integration will back onboarding (future story).[BODY]Correct the blocker references in the ticket's lineage/related context (blocks #7 and #9, not #5 and #8) — or clarify router brief.
- [x]
-
Review: Harbor URL fix (Woodpecker pushing to external Harbor URL)
review-1042-2026-04-21Verdict: APPROVED
Scope is solid. File target verified. SOP violation is real and the fix is single-line, single-file, testable. Ready to advance from backlog to next_up (this project uses backlog as the todo gate).
Template Completeness
Bug template (
template-issue-bug) — all required sections present:- [x] Type: Bug
- [x] Lineage (standalone, cross-referenced to service-onboarding-sop)
- [x] Repo:
forgejo_admin/notion-mcp-remote - [x] What Broke (with offending snippet quoted)
- [x] Repro Steps (3 concrete steps)
- [x] Expected Behavior (explicit target value + SOP reference)
- [x] Environment (cluster/namespace, commit SHA, alerts state)
- [x] Acceptance Criteria (4 items, all testable)
- [x] Related (project, SOP, story)
Traceability
- [x]
story:ops-deploy-gitopslabel — GitOps Deploy via pal-e Platform - [x] story note verified —
story-notion-mcp-remote-ops-deploy-gitopslisted inproject-notion-mcp-remoteuser-stories section. First AC on that story is literally "Woodpecker pipeline builds on push to main, pushes to internal Harbor URL" — this ticket fulfills that AC. - [x]
arch:woodpeckerlabel — Woodpecker CI component - [~] arch note — no dedicated
arch-woodpeckernote in pal-e-docs. Shared Woodpecker architecture is documented inarch-ci-pipeline(pal-e-platform) and the service-specific deployment wiring is inarch-deployment-notion-mcp-remote(which explicitly calls out "must use internal Harbor URL per SOP" for Woodpecker). Non-blocking — consistent with prior review precedent (seereview-882-2026-04-07-v2). - [x] Forgejo issue —
forgejo_admin/notion-mcp-remote#5, open
File Targets
- [x]
.woodpecker.yaml— verified exists at repo root. Offending value confirmed:registry: harbor.tail5b443a.ts.net. - [~] Line reference mismatch — issue body says
.woodpecker.yaml:15but the actual line is:22(line 15 isruff format --check .). Trivial — an implementing agent will find it via the quoted snippet or grep. Not worth blocking.
Repo Placement
Correct.
.woodpecker.yamllives inforgejo_admin/notion-mcp-remote, which is where the issue is filed. No multi-repo spread.Dependencies
No blocking dependencies. This ticket is a leaf — nothing in
in_progressblocks it. It logically unblocks the rest ofstory-ops-deploy-gitops:- #1043 (pal-e-services var.services entry) — wants a working CI push target
- #1044 (pal-e-deployments kustomize overlay) — needs images in Harbor to reference
- #1048 (another Woodpecker-tagged item) — adjacent CI work
Recommend keeping this at the head of the ops-deploy-gitops ordering.
Acceptance Criteria
All 4 AC are verifiable by an agent:
- AC1/AC2 — grep-checkable (
harbor-core.harbor.svc.cluster.localpresent,harbor.tail5b443a.ts.netabsent in.woodpecker.yaml). - AC3 — requires an actual Woodpecker pipeline run on a test branch. Implementing agent should open a test branch, push, and observe the
build-and-pushstep succeed. Note: this assumes the Woodpecker agent can resolveharbor-core.harbor.svc.cluster.local(i.e., it runs inside the cluster). That is the documented assumption per SOP; if it turns out the agent runs externally, AC3 will fail and the fix needs a different shape (e.g., a$HARBOR_INTERNALsecret). The issue already lists$HARBOR_INTERNALas an acceptable alternative, so the scope covers this contingency. - AC4 — regression check on lint/test steps, observable from pipeline output.
Blast Radius
One out-of-scope reference to the external Harbor URL elsewhere in the repo:
k8s/deployment.yaml:23—image: harbor.tail5b443a.ts.net/notion-mcp-remote/notion-mcp-remote:latest
This is the runtime image reference for the Deployment manifest (resolved by kubelet / ArgoCD Image Updater), not a CI push target. The SOP "CI registry URL" check applies only to
.woodpecker.yaml. Whether deployment.yaml should also switch to the internal URL is a separate design question (depends on whether kubelet/ArgoCD can resolve cluster-internal DNS for image pulls, and on Image Updater's configured registry). Flagging as informational — do NOT expand this ticket's scope. If it needs fixing, file a separate issue.No similar pattern in sibling repos within this project (shared infra repos out of scope).
Decomposition Assessment
1 file modified, 4 AC, estimated <5 min agent work. Well under the decomposition threshold. No decomposition needed.
Recommendation
APPROVED — no blocking changes required. Two optional polish items the implementing agent may handle inline without re-review:
[BODY-OPTIONAL]Update the line reference in the issue body from.woodpecker.yaml:15to.woodpecker.yaml:22. Trivial; the quoted snippet makes the target unambiguous regardless.[SCOPE-FUTURE]Consider filing a follow-up ticket to evaluate whetherk8s/deployment.yamlshould also use the internal Harbor URL for image pulls. Out of scope for this bug; do not bundle.
Item #1042 is ready to advance from backlog to next_up.
Architecture 3
-
Deployment: notion-mcp-remote
arch-deployment-notion-mcp-remoteDeployment: notion-mcp-remote
Diagram
graph TB CAI[claude.ai] TF[Tailscale Funnelnotion-mcp-remote.tail5b443a.ts.net] ING[Traefik ingress] SVC[Service notion-mcp-remote] POD[Pod notion-mcp-remote :8000] PVC[(PVC tokens.json)] NAPI[api.notion.com] HAR[Harbor Registry] WP[Woodpecker CI] ACD[ArgoCD] IU[ArgoCD Image Updater] OVL[overlays/notion-mcp-remote/prodpal-e-deployments] TFVAR[pal-e-services var.services] CAI -->|HTTPS| TF TF --> ING ING --> SVC SVC --> POD POD --> PVC POD -->|egress 443| NAPI WP -->|build + push| HAR IU -->|watch tags| HAR IU -->|write newTag| OVL ACD -->|sync| OVL ACD -->|apply| POD TFVAR -->|terraform| ACD TFVAR -->|terraform| HAR TFVAR -->|terraform| TFComponents
Component Purpose Notes Tailscale Funnel Public ingress at notion-mcp-remote.tail5b443a.ts.netProvisioned by pal-e-services terraform when funnel=trueNamespace + Service + Pod Standard k8s trio; app listens on :8000 Dockerfile.k8s EXPOSE 8000 PVC Persists data/tokens.jsonacross pod restartsk8s/pvc.yaml; single-writerHarbor Private image registry; project notion-mcp-remoteCreated by tofu apply; robot creds via Woodpecker secretsWoodpecker CI Builds on push to main, pushes SHA + latest tags .woodpecker.yaml; must use internal Harbor URL per SOPArgoCD + Image Updater Watches Harbor tags, writes newTag to overlay kustomization.yaml, syncs Application managed by pal-e-services terraform pal-e-services var.services Declarative service registration: image, port, funnel, overlay path k3s.tfvars; PR-gatedpal-e-deployments overlay Kustomize overlay at overlays/notion-mcp-remote/prod/kustomization.yaml + deployment-patch.yaml; no secrets in git Key Decisions
- Tailscale Funnel for public exposure — same pattern as other remote MCP services. No separate domain or cert management.
- Kustomize overlay, not inline in pal-e-services — per
convention-kustomize-overlay. Image Updater writes newTag into the overlay so ArgoCD sees declarative tag pins. - Secrets created via
kubectlbefore first sync — overlay must not contain real secret data or ArgoCD will clobber on sync (service-onboarding-sop warning). - No Keycloak client — auth is Notion OAuth end-to-end; no pal-e identity layer needed for this service.
- No NetworkPolicy changes — service egresses only to
api.notion.com; no MinIO/Postgres/Keycloak dependencies.
Related
arch-domain-notion-mcp-remotearch-dataflow-notion-mcp-remoteproject-notion-mcp-remoteservice-onboarding-sopconvention-kustomize-overlay
-
Data Flow: notion-mcp-remote
arch-dataflow-notion-mcp-remoteData Flow: notion-mcp-remote
Diagram
sequenceDiagram participant C as claude.ai participant R as notion-mcp-remote participant S as TokenStore participant N as Notion OAuth participant API as Notion API Note over C,R: 1. Dynamic Client Registration C->>R: POST /register R->>S: persist DynamicClient R-->>C: client_id Note over C,R: 2. OAuth authorize C->>R: GET /authorize (code_challenge, state) R->>N: 302 to Notion consent N-->>C: consent UI C->>N: approve N->>R: GET /callback (code) R->>N: POST /v1/oauth/token (code) N-->>R: access_token + owner + workspace R->>S: persist AccessToken (notion_token, workspace, 1y) R-->>C: 302 with MCP auth code C->>R: POST /token (code) R-->>C: access_token (mcp_token), ttl 1y Note over C,R: 3. Tool call C->>R: POST /mcp (Bearer mcp_token) R->>S: load_access_token(mcp_token) R->>R: _setup_notion_client — ContextVar.set(NotionClient(notion_token)) R->>R: MCP tool handler runs R->>API: REST call with notion_token API-->>R: JSON result R-->>C: MCP tool responseComponents
Component Purpose Notes Dynamic Client Registration claude.ai registers itself with no prior configuration Standard MCP DCR; handled by mcp-remote-auth Authorize/Callback Three-legged OAuth proxy: claude.ai → notion-mcp-remote → Notion notion-mcp-remote is both an OAuth server (to claude.ai) and an OAuth client (to Notion) Token exchange Converts Notion OAuth response into an MCP access token that wraps the notion_token upstream_token_key = "notion_token"ContextVar injection Per-request NotionClient isolation set via client_patch.set_client_for_requestTool handlers The 26 notion_mcp tools, imported unchanged apply_patch()swaps their get_client before tools registerKey Decisions
- OAuth proxy (not pass-through) — issue our own MCP tokens rather than handing the raw Notion token to claude.ai. Lets us rotate independently and audit per-client.
- ContextVar over global state — required because multiple concurrent requests may hit the same process with different user tokens (story: multi-workspace-safety).
- apply_patch before notion_mcp import — notion_mcp registers tools eagerly at import; the monkey-patch must land first or tool modules capture the unpatched get_client.
- stateless_http = False — claude.ai needs session affinity; fixed in PR #17/18.
Related
arch-domain-notion-mcp-remotearch-deployment-notion-mcp-remoteproject-notion-mcp-remote
-
Domain Model: notion-mcp-remote
arch-domain-notion-mcp-remoteDomain Model: notion-mcp-remote
Diagram
erDiagram DynamicClient ||--o{ AuthCode : issues DynamicClient ||--o{ AccessToken : owns AuthCode ||--o| AccessToken : "exchanges for" AccessToken }o--|| NotionWorkspace : "grants access to" AccessToken }o--|| User : "belongs to" User }o--|| NotionWorkspace : "member of" AccessToken { string token PK datetime expires_at "1 year default" string notion_token "upstream OAuth token" string identity "user (workspace)" } DynamicClient { string client_id PK string redirect_uri string client_name } AuthCode { string code PK datetime expires_at "short lived" } NotionWorkspace { string workspace_id PK string workspace_name } User { string user_id PK string name }Components
Component Purpose Notes DynamicClient An MCP client registered via Dynamic Client Registration (e.g., claude.ai) Persisted by TokenStore; one per MCP client instance AuthCode Short-lived code issued during authorize, exchanged for an access token Standard OAuth authz code flow AccessToken Long-lived (1 yr) MCP access token; wraps the upstream Notion token and workspace identity PR #20; lifetime = 31536000s NotionWorkspace The workspace an AccessToken grants access to Bound at OAuth consent time; 1:1 with the integration install User The Notion user who granted consent Extracted via extract_identity_from_token_responseKey Decisions
- AccessToken owns an upstream notion_token — MCP token and Notion OAuth token are distinct. MCP token is the client's key; the notion_token is consumed server-side. Lets us rotate MCP tokens without re-auth against Notion.
- 1-year access token lifetime — PR #20. Chosen so claude.ai users rarely re-auth. No refresh tokens because Notion OAuth does not issue them for integrations.
- File-backed TokenStore — JSON on PVC today. Moving to a DB or cluster Secret is future work if multi-replica or HA becomes necessary.
Related
arch-dataflow-notion-mcp-remotearch-deployment-notion-mcp-remoteproject-notion-mcp-remote
User Story 3
-
GitOps Deploy via pal-e Platform
story-notion-mcp-remote-ops-deploy-gitopsstory: GitOps Deploy via pal-e Platform
Role
Operator — responsible for keeping notion-mcp-remote running, observable, and SOP-compliant inside the pal-e cluster.
Key
ops-deploy-gitopsWant
As an operator, I want notion-mcp-remote to deploy via the standard pal-e pipeline (Woodpecker → Harbor → ArgoCD) with health + metrics endpoints wired to ServiceMonitor.
So That
So that the service meets service-onboarding-sop, is visible in the platform's observability stack, and has zero bespoke deploy paths to maintain.
Acceptance Criteria
- [ ] Woodpecker pipeline builds on push to main, pushes to internal Harbor URL
- [ ] pal-e-services
var.servicesentry landed via PR - [ ] pal-e-deployments kustomize overlay landed via PR
- [ ]
tofu applycreates namespace, ArgoCD Application, Harbor project + robots, Tailscale Funnel ingress - [ ] Secrets provisioned via
kubectl create secret genericbefore first ArgoCD sync - [ ] ArgoCD reports Application healthy and synced
- [ ] ServiceMonitor scrapes
/metricsand Prometheus seesnotion_mcp_remote_up 1
Success Metric
pre-deploy-validation checklist from service-onboarding-sop is 100% green before first apply. Zero manual kubectl edits post-deploy.
Related Architecture
arch-deployment-notion-mcp-remote
Related
project-notion-mcp-remoteboard-notion-mcp-remoteservice-onboarding-sop
-
Per-Request Workspace Isolation
story-notion-mcp-remote-multi-workspace-safetystory: Per-Request Workspace Isolation
Role
Operator — responsible for tenant safety when multiple users or workspaces connect to the same deployment.
Key
multi-workspace-safetyWant
As an operator, I want each OAuth-authenticated request to use only its own user's Notion token.
So That
So that one deployment can serve multiple users and workspaces without any cross-tenant data bleed.
Acceptance Criteria
- [ ] The
NotionClientfor a request is set via aContextVarat auth-resolution time and cleared at request end - [ ]
notion_mcp.server.get_clientand all 6 tool modules are patched to read the ContextVar, not a shared global - [ ] Two concurrent requests with different workspaces return the correct per-request data in tests
- [ ] No global singleton
NotionClientexists anywhere in the runtime path
Success Metric
Zero cross-tenant incidents. A concurrency test with 2+ workspaces returns workspace-correct results 100% of the time.
Related Architecture
arch-domain-notion-mcp-remote— AccessToken binds to a single NotionWorkspace and Userarch-dataflow-notion-mcp-remote— per-request ContextVar set in the tool-call sequence
Related
project-notion-mcp-remoteboard-notion-mcp-remote
- [ ] The
-
Connect Notion to claude.ai
story-notion-mcp-remote-claude-ai-connectstory: Connect Notion to claude.ai
Role
Operator (Lucas) using claude.ai as an MCP client.
Key
claude-ai-connectWant
As a claude.ai user, I want to paste a public URL into claude.ai's MCP integration settings and complete Notion OAuth.
So That
So that I can use Notion tools inside claude.ai chats without running a local stdio server or juggling integration tokens.
Acceptance Criteria
- [ ] Public HTTPS URL resolves and
/healthreturns 200 - [ ]
/.well-known/oauth-authorization-serverreturns valid MCP OAuth metadata - [ ] claude.ai accepts the URL as an MCP server
- [ ] Notion OAuth handshake completes (consent screen → code → token) without error
- [ ] After OAuth, a
searchtool call in a claude.ai chat returns live results from the authorized workspace - [ ] Access token lifetime is 1 year so reconnection is rare
Success Metric
Lucas pastes the URL into claude.ai, completes OAuth in under 60 seconds, and runs a Notion tool successfully on the first attempt.
Related Architecture
arch-dataflow-notion-mcp-remote— OAuth handshake and tool-call sequencesarch-deployment-notion-mcp-remote— Tailscale Funnel and ingress that expose the URL
Related
project-notion-mcp-remote— project pageboard-notion-mcp-remote— board that executes this story
- [ ] Public HTTPS URL resolves and
Board 1
-
notion-mcp-remote
board-notion-mcp-remoteNo content