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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .codex-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
],
"brandColor": "#4A90D9",
"composerIcon": "./assets/icon.png",
"logo": "./assets/logo.svg"
"logo": "./assets/logo.svg",
"screenshots": ["./assets/screenshots/overview.png"]
},
"author": {
"name": "Jacob Magar",
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,19 @@ repos:
files: 'skills/.*\.md$'
- id: docker-security
name: docker-security
entry: bash scripts/check-docker-security.sh
entry: bash bin/check-docker-security.sh
language: system
files: 'Dockerfile$'
pass_filenames: true
- id: no-baked-env
name: no-baked-env
entry: bash scripts/check-no-baked-env.sh .
entry: bash bin/check-no-baked-env.sh .
language: system
files: '(Dockerfile|docker-compose\.yaml|\.dockerignore|entrypoint\.sh)$'
pass_filenames: false
- id: ensure-ignore-files
name: ensure-ignore-files
entry: bash scripts/ensure-ignore-files.sh --check .
entry: bash bin/ensure-ignore-files.sh --check .
language: system
files: '(\.gitignore|\.dockerignore)$'
pass_filenames: false
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ Versioning follows [Semantic Versioning](https://semver.org/).

---

## [1.1.3-scaffold] - 2026-03-31

### Changed

- **Migration:** Renamed env var `AUTH_TOKEN` → `ARCANE_MCP_TOKEN` for consistency with the
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ ENV RUNNING_IN_DOCKER=true
COPY --chown=node:node --from=builder /app/node_modules ./node_modules
COPY --chown=node:node --from=builder /app/dist ./dist
COPY --chown=node:node --from=builder /app/package.json ./
RUN mkdir -p /app/logs /app/.cache \
RUN mkdir -p /app/logs /app/backups /app/.cache \
&& chown -R node:node /app
USER 1000:1000
EXPOSE 3000
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2026 gotify-mcp contributors
Copyright (c) 2026 arcane-mcp contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
97 changes: 97 additions & 0 deletions arcane.subdomain.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
## Version 2026/03/31 - MCP 2025-11-25 SWAG Compatible
# MCP Streamable-HTTP Reverse Proxy
# Service: arcane
# Domain: arcane.example.com
# Upstream MCP: http://100.64.0.1:44332

server {
listen 443 ssl;
listen [::]:443 ssl;

server_name arcane.example.com;

include /config/nginx/ssl.conf;

client_max_body_size 0;

# MCP server upstream (Tailscale IP of the host running arcane-mcp)
set $mcp_upstream_app "100.64.0.1";
set $mcp_upstream_port "44332";
set $mcp_upstream_proto "http";

# DNS rebinding protection
set $origin_valid 0;
if ($http_origin = "") { set $origin_valid 1; }
if ($http_origin = "https://$server_name") { set $origin_valid 1; }
if ($http_origin ~ "^https://(localhost|127\.0\.0\.1)(:[0-9]+)?$") { set $origin_valid 1; }
if ($http_origin ~ "^https://(.*\.)?anthropic\.com$") { set $origin_valid 1; }
if ($http_origin ~ "^https://(.*\.)?claude\.ai$") { set $origin_valid 1; }

add_header X-MCP-Version "2025-11-25" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

# Uncomment for auth provider (authelia, authentik, etc.)
# include /config/nginx/authelia-server.conf;

# OAuth 2.1: /_oauth_verify, /.well-known/*, /jwks, /register,
# /authorize, /token, /revoke, /callback, /success, error pages
include /config/nginx/oauth.conf;

location /mcp {
if ($origin_valid = 0) {
add_header Content-Type "application/json" always;
return 403 '{"error":"origin_not_allowed","message":"Origin header validation failed"}';
}

auth_request /_oauth_verify;
auth_request_set $auth_status $upstream_status;

include /config/nginx/resolver.conf;
include /config/nginx/proxy.conf;
include /config/nginx/mcp.conf;

proxy_pass $mcp_upstream_proto://$mcp_upstream_app:$mcp_upstream_port;
}

location /health {
include /config/nginx/resolver.conf;

proxy_set_header Accept "application/json";
proxy_set_header X-Health-Check "nginx-mcp-proxy";

proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 10s;

add_header Cache-Control "no-cache, no-store, must-revalidate" always;
add_header Pragma "no-cache" always;

proxy_pass $mcp_upstream_proto://$mcp_upstream_app:$mcp_upstream_port;
}

location ~* ^/(session|sessions) {
auth_request /_oauth_verify;
auth_request_set $auth_status $upstream_status;

include /config/nginx/resolver.conf;
include /config/nginx/proxy.conf;

proxy_set_header MCP-Protocol-Version $http_mcp_protocol_version;
proxy_set_header Mcp-Session-Id $http_mcp_session_id;

add_header Cache-Control "no-store" always;
add_header Pragma "no-cache" always;

proxy_pass $mcp_upstream_proto://$mcp_upstream_app:$mcp_upstream_port;
}

location / {
# Uncomment for auth provider
# include /config/nginx/authelia-location.conf;

include /config/nginx/resolver.conf;
include /config/nginx/proxy.conf;

proxy_pass $mcp_upstream_proto://$mcp_upstream_app:$mcp_upstream_port;
}
}
Empty file added backups/.gitkeep
Empty file.
5 changes: 3 additions & 2 deletions bin/check-docker-security.sh
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,9 @@ else
fi

# Check there's no USER root in the final (runtime) stage
if echo "$RUNTIME_SECTION" | grep -qE '^USER\s+root\s*$'; then
fail "No root in runtime" "USER root found in final stage — never run as root in production"
# Match: USER root, USER 0, USER 0:0, USER 0:anything
if echo "$RUNTIME_SECTION" | grep -qE '^USER[[:space:]]+(root|0(:[0-9]*)?)([[:space:]]*)$'; then
fail "No root in runtime" "USER root/0 found in final stage — never run as root in production"
else
pass "No root in runtime stage"
fi
Expand Down
2 changes: 1 addition & 1 deletion bin/check-no-baked-env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ fi
# Check CMD and ENTRYPOINT directives in Dockerfile for baked-in credentials.
# Pattern: bare token/key/password/secret followed by a non-variable value.
if [[ -f "$DOCKERFILE" ]]; then
CRED_PATTERNS='(password|secret|token|api[_-]?key)[=: ]+[^${\s]'
CRED_PATTERNS='(password|secret|token|api[_-]?key)[=: ]+[^${[:space:]]'
HARDCODED_CREDS=$(grep -inE "^(CMD|ENTRYPOINT)\s+.*${CRED_PATTERNS}" "$DOCKERFILE" || true)
if [[ -n "$HARDCODED_CREDS" ]]; then
fail "No hardcoded creds in Dockerfile CMD/ENTRYPOINT" "Found suspicious hardcoded values:"
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ volumes:

networks:
jakenet:
name: ${DOCKER_NETWORK:-arcane-mcp}
name: ${DOCKER_NETWORK:-arcane_mcp}
external: true
Comment on lines +35 to +38
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Network name and variable default are inconsistent.

The network is declared with name: ${DOCKER_NETWORK:-arcane-mcp}, but the service references jakenet as the network key. The name attribute sets the actual Docker network name, which defaults to arcane-mcp, while jakenet is just an internal compose reference.

This works, but the naming is confusing—jakenet appears to be a leftover or project-specific name. Consider aligning the key name with the default network name for clarity.

♻️ Suggested rename for consistency
     networks:
-      - jakenet
+      - arcane-mcp-net

 networks:
-  jakenet:
+  arcane-mcp-net:
     name: ${DOCKER_NETWORK:-arcane-mcp}
     external: true
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yaml` around lines 35 - 38, The network key name "jakenet" is
misleading versus the actual Docker network name set by name:
${DOCKER_NETWORK:-arcane-mcp}; rename the compose network key from jakenet to a
name that matches the default (e.g., arcane-mcp or docker_network) and update
any service network references that use "jakenet" accordingly so the internal
compose key and the configured default network name
(${DOCKER_NETWORK:-arcane-mcp}) are consistent.

21 changes: 19 additions & 2 deletions hooks/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,25 @@
"hooks": [
{
"type": "command",
"command": "diff -q \"${CLAUDE_PLUGIN_ROOT}/package.json\" \"${CLAUDE_PLUGIN_DATA}/package.json\" >/dev/null 2>&1 || (cd \"${CLAUDE_PLUGIN_DATA}\" && cp \"${CLAUDE_PLUGIN_ROOT}/package.json\" . && npm install) || rm -f \"${CLAUDE_PLUGIN_DATA}/package.json\"",
"timeout": 60
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/sync-env.sh",
"timeout": 10
},
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/ensure-ignore-files.sh",
"timeout": 5
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit|Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/scripts/fix-env-perms.sh",
"timeout": 5
}
]
}
Expand Down
Loading
Loading