appknox-mcp 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. appknox_mcp-1.0.0/.claude-plugin/marketplace.json +14 -0
  2. appknox_mcp-1.0.0/.claude-plugin/plugin.json +8 -0
  3. appknox_mcp-1.0.0/.github/workflows/ci.yml +50 -0
  4. appknox_mcp-1.0.0/.github/workflows/publish.yml +113 -0
  5. appknox_mcp-1.0.0/.gitignore +13 -0
  6. appknox_mcp-1.0.0/.mcp.json +12 -0
  7. appknox_mcp-1.0.0/.pre-commit-config.yaml +34 -0
  8. appknox_mcp-1.0.0/CONTRIBUTING.md +53 -0
  9. appknox_mcp-1.0.0/INSTALL.md +329 -0
  10. appknox_mcp-1.0.0/LICENSE +14 -0
  11. appknox_mcp-1.0.0/PKG-INFO +269 -0
  12. appknox_mcp-1.0.0/README.md +243 -0
  13. appknox_mcp-1.0.0/agents/appknox-fixer.md +53 -0
  14. appknox_mcp-1.0.0/commands/fix.md +48 -0
  15. appknox_mcp-1.0.0/commands/triage.md +61 -0
  16. appknox_mcp-1.0.0/commands/upload.md +48 -0
  17. appknox_mcp-1.0.0/commands/verify.md +45 -0
  18. appknox_mcp-1.0.0/pyproject.toml +78 -0
  19. appknox_mcp-1.0.0/scripts/configure_mcp.py +21 -0
  20. appknox_mcp-1.0.0/scripts/install.ps1 +241 -0
  21. appknox_mcp-1.0.0/scripts/install.sh +250 -0
  22. appknox_mcp-1.0.0/scripts/release.sh +72 -0
  23. appknox_mcp-1.0.0/scripts/uninstall.ps1 +61 -0
  24. appknox_mcp-1.0.0/scripts/uninstall.sh +49 -0
  25. appknox_mcp-1.0.0/src/appknox_mcp/__init__.py +0 -0
  26. appknox_mcp-1.0.0/src/appknox_mcp/app.py +20 -0
  27. appknox_mcp-1.0.0/src/appknox_mcp/client.py +205 -0
  28. appknox_mcp-1.0.0/src/appknox_mcp/configure.py +377 -0
  29. appknox_mcp-1.0.0/src/appknox_mcp/findings.py +166 -0
  30. appknox_mcp-1.0.0/src/appknox_mcp/instructions.py +117 -0
  31. appknox_mcp-1.0.0/src/appknox_mcp/models.py +146 -0
  32. appknox_mcp-1.0.0/src/appknox_mcp/resolve.py +52 -0
  33. appknox_mcp-1.0.0/src/appknox_mcp/server.py +179 -0
  34. appknox_mcp-1.0.0/src/appknox_mcp/status.py +115 -0
  35. appknox_mcp-1.0.0/src/appknox_mcp/upload.py +93 -0
  36. appknox_mcp-1.0.0/src/appknox_mcp/verify.py +159 -0
  37. appknox_mcp-1.0.0/tests/conftest.py +13 -0
  38. appknox_mcp-1.0.0/tests/test_client.py +190 -0
  39. appknox_mcp-1.0.0/tests/test_configure.py +422 -0
  40. appknox_mcp-1.0.0/tests/test_findings.py +281 -0
  41. appknox_mcp-1.0.0/tests/test_resolve.py +149 -0
  42. appknox_mcp-1.0.0/tests/test_server.py +159 -0
  43. appknox_mcp-1.0.0/tests/test_status.py +92 -0
  44. appknox_mcp-1.0.0/tests/test_upload.py +157 -0
  45. appknox_mcp-1.0.0/tests/test_verify.py +136 -0
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "appknox",
3
+ "description": "Appknox MCP server and Claude Code plugin (slash commands + fixer agent).",
4
+ "owner": {
5
+ "name": "Appknox"
6
+ },
7
+ "plugins": [
8
+ {
9
+ "name": "appknox",
10
+ "source": "./",
11
+ "description": "Appknox KnoxIQ fix-and-verify: fetch the vulnerabilities Appknox found in your app, fix them in your repo, verify with the finding's PoC, and re-scan."
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "appknox",
3
+ "description": "Appknox KnoxIQ fix-and-verify: fetch the vulnerabilities Appknox found in your app, fix them in your repo, verify with the finding's PoC, and re-scan.",
4
+ "version": "0.1.0",
5
+ "author": {
6
+ "name": "Appknox"
7
+ }
8
+ }
@@ -0,0 +1,50 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [develop]
6
+ push:
7
+ branches: [develop]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v5
16
+ - name: Run tests
17
+ run: uv run pytest -q
18
+
19
+ lint:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+ - name: Install uv
24
+ uses: astral-sh/setup-uv@v5
25
+ # Runs the exact hooks in .pre-commit-config.yaml (black, ruff, mypy,
26
+ # plus the hygiene checks) — a pre-commit hook is opt-in locally and
27
+ # skippable with --no-verify, so this is what actually enforces it.
28
+ - uses: actions/cache@v4
29
+ with:
30
+ path: ~/.cache/pre-commit
31
+ key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}
32
+ - name: Run pre-commit hooks
33
+ run: uv run --with pre-commit pre-commit run --all-files --show-diff-on-failure
34
+
35
+ # Single required status check for branch protection, instead of naming
36
+ # `test`/`lint` individually there — this succeeds only if every job in the
37
+ # workflow did, so adding a new job here later doesn't also require a
38
+ # branch-protection settings change to actually gate merges on it.
39
+ ci:
40
+ name: CI
41
+ runs-on: ubuntu-latest
42
+ needs: [test, lint]
43
+ if: always()
44
+ steps:
45
+ - name: Check all jobs succeeded
46
+ run: |
47
+ if [ "${{ needs.test.result }}" != "success" ] || [ "${{ needs.lint.result }}" != "success" ]; then
48
+ echo "::error::One or more required CI jobs failed (test=${{ needs.test.result }}, lint=${{ needs.lint.result }})."
49
+ exit 1
50
+ fi
@@ -0,0 +1,113 @@
1
+ name: Build, Attach & Publish Release
2
+
3
+ # Fires when a GitHub Release is published — created by YOU (GitHub UI's
4
+ # "Draft a new release", or `gh release create <tag>` from your own login),
5
+ # never by this workflow itself. That distinction matters: GitHub's
6
+ # anti-recursion guard blocks an event created by a workflow's own
7
+ # GITHUB_TOKEN from triggering other workflows, so a release this workflow
8
+ # created would never actually fire `release: published` — which is exactly
9
+ # why release creation now has to be a human action, not automated. In
10
+ # exchange, you get full control: pick any tag (new or existing) and any
11
+ # branch/commit as the release's target, from GitHub's own release UI.
12
+ #
13
+ # Verifies pyproject.toml's version at that exact commit matches the tag you
14
+ # chose (a mismatch almost always means you forgot to bump the version before
15
+ # tagging), then tests, builds the wheel + sdist, smoke-tests the BUILT wheel,
16
+ # attaches both to the release you created, and publishes:
17
+ # - "Set as a pre-release" checked -> TestPyPI (safe, repeatable dry run)
18
+ # - unchecked (a real release) -> PyPI
19
+ #
20
+ # Requires one-time setup on both index's side before this can succeed —
21
+ # nothing here works until you do this by hand (PyPI Trusted Publishing has no
22
+ # API for it):
23
+ # 1. On test.pypi.org and pypi.org, under your account -> Publishing, add a
24
+ # "pending" trusted publisher for this project:
25
+ # - PyPI project name: appknox-mcp
26
+ # - Owner: appknox Repository: appknox-mcp
27
+ # - Workflow filename: publish.yml
28
+ # - Environment name: testpypi (on test.pypi.org) / pypi (on pypi.org)
29
+ # 2. In this repo's Settings -> Environments, create two environments named
30
+ # "testpypi" and "pypi" (the names must match what you registered above).
31
+ # Optionally add required reviewers to the "pypi" environment for a manual
32
+ # gate on real publishes.
33
+ # No secrets to create or store — Trusted Publishing uses this job's own OIDC
34
+ # token (the `id-token: write` permission below), which is why the workflow
35
+ # filename and environment name both have to match exactly what you registered.
36
+ on:
37
+ release:
38
+ types: [published]
39
+
40
+ jobs:
41
+ publish:
42
+ runs-on: ubuntu-latest
43
+ # Has to be resolved here, not inside a step — environment protection
44
+ # rules are evaluated before the job's steps run. github.event.release is
45
+ # available directly since this triggers on the release event itself —
46
+ # no separate lookup job needed (unlike when we reacted to a workflow_run).
47
+ environment: ${{ github.event.release.prerelease && 'testpypi' || 'pypi' }}
48
+ permissions:
49
+ contents: write # to attach the wheel/sdist to the release you created
50
+ id-token: write # PyPI/TestPyPI Trusted Publishing (OIDC)
51
+ env:
52
+ # Dummy token so importing the server / running tests never blocks on
53
+ # auth. Not a credential — the server accepts an empty/placeholder
54
+ # token and only fails on an actual API call, which this never makes.
55
+ APPKNOX_ACCESS_TOKEN: "PLACEHOLDER:PLACEHOLDER"
56
+ steps:
57
+ - uses: actions/checkout@v4
58
+ with:
59
+ # Build exactly what you tagged, not whatever the default branch's
60
+ # HEAD happens to be.
61
+ ref: ${{ github.event.release.tag_name }}
62
+
63
+ - name: Install uv
64
+ uses: astral-sh/setup-uv@v6
65
+
66
+ - name: Verify pyproject.toml's version matches the release tag
67
+ run: |
68
+ EXPECTED="v$(uv version --short)"
69
+ TAG="${{ github.event.release.tag_name }}"
70
+ if [ "$TAG" != "$EXPECTED" ]; then
71
+ echo "::error::Release tag '$TAG' doesn't match pyproject.toml's version"\
72
+ "('$EXPECTED' at this commit). Bump the version to match before"\
73
+ "tagging, or delete this release and re-tag."
74
+ exit 1
75
+ fi
76
+
77
+ - name: Run tests
78
+ run: uv run pytest -q
79
+
80
+ - name: Build wheel + sdist
81
+ run: uv build
82
+
83
+ - name: Smoke-test the built wheel (import + tool count) in a clean venv
84
+ run: |
85
+ uv venv /tmp/verify
86
+ uv pip install --python /tmp/verify/bin/python dist/*.whl
87
+ # Run from /tmp so a source-relative file (e.g. the repo's docs/) can't
88
+ # mask a packaging bug — this is exactly what caught the spec-load crash.
89
+ cd /tmp
90
+ /tmp/verify/bin/python -c "import asyncio, appknox_mcp.server as s; n=len(asyncio.run(s.mcp.list_tools())); print(f'built wheel OK — {n} tools'); assert n >= 7, n"
91
+
92
+ - name: Attach wheel + sdist to the release
93
+ env:
94
+ GH_TOKEN: ${{ github.token }}
95
+ run: gh release upload "${{ github.event.release.tag_name }}" dist/*.whl dist/*.tar.gz --clobber
96
+
97
+ - name: Publish to TestPyPI (prerelease dry run)
98
+ if: github.event.release.prerelease
99
+ run: uv publish --index testpypi
100
+
101
+ - name: Publish to PyPI
102
+ if: ${{ !github.event.release.prerelease }}
103
+ run: uv publish
104
+
105
+ - name: Summary
106
+ run: |
107
+ if [ "${{ github.event.release.prerelease }}" = "true" ]; then
108
+ echo "### Published to TestPyPI :test_tube:" >> "$GITHUB_STEP_SUMMARY"
109
+ echo "Verify with: \`uv tool install --reinstall --index https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ --index-strategy unsafe-best-match appknox-mcp\`" >> "$GITHUB_STEP_SUMMARY"
110
+ else
111
+ echo "### Published to PyPI :rocket:" >> "$GITHUB_STEP_SUMMARY"
112
+ echo "Verify with: \`uv tool install --reinstall appknox-mcp\`" >> "$GITHUB_STEP_SUMMARY"
113
+ fi
@@ -0,0 +1,13 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ *.egg-info/
5
+ dist/
6
+ build/
7
+ .env
8
+ .python-version
9
+ uv.lock
10
+ .zed/
11
+ .DS_Store
12
+ CLAUDE.md
13
+ .claude/
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "appknox": {
4
+ "command": "uv",
5
+ "args": ["run", "appknox-mcp"],
6
+ "env": {
7
+ "APPKNOX_ACCESS_TOKEN": "${APPKNOX_ACCESS_TOKEN}",
8
+ "APPKNOX_BASE_URL": "${APPKNOX_BASE_URL}"
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,34 @@
1
+ # Installed with `uv run pre-commit install` (one-time, per clone) so these
2
+ # run automatically on `git commit`. CI (.github/workflows/ci.yml) runs the
3
+ # same checks via `uv run pre-commit run --all-files` — a pre-commit hook is
4
+ # opt-in locally and skippable with `--no-verify`, so CI is what actually
5
+ # enforces this on every PR, not just a courtesy for whoever remembers to
6
+ # install it.
7
+ repos:
8
+ - repo: https://github.com/pre-commit/pre-commit-hooks
9
+ rev: v5.0.0
10
+ hooks:
11
+ - id: trailing-whitespace
12
+ - id: end-of-file-fixer
13
+ - id: check-yaml
14
+ - id: check-toml
15
+ - id: check-merge-conflict
16
+ - id: check-added-large-files
17
+
18
+ - repo: https://github.com/psf/black
19
+ rev: 25.9.0
20
+ hooks:
21
+ - id: black
22
+
23
+ - repo: https://github.com/astral-sh/ruff-pre-commit
24
+ rev: v0.16.7
25
+ hooks:
26
+ - id: ruff-check
27
+ args: [--fix]
28
+
29
+ - repo: https://github.com/pre-commit/mirrors-mypy
30
+ rev: v1.18.2
31
+ hooks:
32
+ - id: mypy
33
+ additional_dependencies: ["fastmcp>=2.0.0,<4.0.0", "httpx>=0.27.0", "pydantic>=2.0.0"]
34
+ files: ^src/
@@ -0,0 +1,53 @@
1
+ # Contributing
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ uv sync
7
+ uv run pre-commit install
8
+ ```
9
+
10
+ The second command wires up git's `pre-commit` hook so black, ruff, and mypy
11
+ (plus a few hygiene checks) run automatically on every `git commit` — see
12
+ `.pre-commit-config.yaml`. It only touches files you've staged, so it's fast.
13
+
14
+ To run everything by hand (e.g. before opening a PR):
15
+
16
+ ```bash
17
+ uv run pre-commit run --all-files
18
+ ```
19
+
20
+ CI runs this same command on every PR — a local git hook is opt-in and
21
+ skippable with `git commit --no-verify`, so CI is what actually enforces it.
22
+
23
+ ## Branch rules
24
+
25
+ `develop` is the default branch and requires at least one approving review
26
+ plus a green CI run before a PR can merge — direct pushes aren't allowed for
27
+ regular contributors. Target `develop` with your PR unless you're stacking on
28
+ another open PR. (Repo admins can still push directly — see Releasing below,
29
+ which relies on exactly that to bump the version with no PR round-trip.)
30
+
31
+ ## Tests
32
+
33
+ ```bash
34
+ uv run pytest -q
35
+ ```
36
+
37
+ ## Releasing
38
+
39
+ ```bash
40
+ ./scripts/release.sh patch # or minor / major
41
+ ./scripts/release.sh patch --real # skip TestPyPI, publish straight to PyPI
42
+ ```
43
+
44
+ Bumps `pyproject.toml`'s version, pushes it straight to `develop`, tags it,
45
+ and creates a GitHub Release using your own `gh` login — which is what
46
+ triggers `.github/workflows/publish.yml` to actually build, test, and publish
47
+ (TestPyPI by default, or real PyPI with `--real`). No PAT or stored secret
48
+ involved: since it's your own account doing the push and creating the
49
+ release, branch protection's admin exemption and GitHub's normal "a human did
50
+ this" behavior both apply, exactly as if you'd done each step by hand.
51
+
52
+ Must be run from a clean `develop` checkout, by an account with admin access
53
+ to this repo (needed to push directly, bypassing the PR requirement above).
@@ -0,0 +1,329 @@
1
+ # Installing the Appknox MCP server
2
+
3
+ This document is written so an **AI coding agent** (Claude Code, Cursor, Codex, …)
4
+ can install and wire up the Appknox MCP server end-to-end. A human can follow it
5
+ too. It needs no clone of this repo — the server installs as a global command,
6
+ and this exact file ships inside the package: once step 1 below is done, run
7
+ `appknox-mcp --install-guide` any time to print it again.
8
+
9
+ > **Agent:** if a user handed you this repo's URL (or this file) and said "install
10
+ > it," you're in the right place — do the steps below in order. **First identify
11
+ > which client you are actually running as** (Claude Code, Cursor, Codex, GitHub
12
+ > Copilot CLI, VS Code, Windsurf, Claude Desktop, or something else) — you need
13
+ > this for step 3 regardless, and it matters more once you reach the section
14
+ > titled "Claude Code only": if you are not Claude Code, skip that entire
15
+ > section (do not run its commands "just in case" or because it looked like the
16
+ > next step in order) and go straight to the step after it. The only things to
17
+ > ask the user for are their **Access Key ID + Secret Access Key** (step 2) and,
18
+ > if it's unclear, **which client** to configure. Never print the secret back.
19
+
20
+ ## 0. Prerequisite: `uv`
21
+
22
+ If `uv` is not on PATH, install it:
23
+
24
+ ```bash
25
+ curl -LsSf https://astral.sh/uv/install.sh | sh # macOS/Linux
26
+ # Windows PowerShell: powershell -c "irm https://astral.sh/uv/install.ps1 | iex"
27
+ ```
28
+
29
+ Then ensure `~/.local/bin` is on PATH (the installer usually handles this).
30
+
31
+ ## 1. Install the server as a global tool
32
+
33
+ **You (the agent) run these commands** — the user doesn't. Try them **in order,
34
+ stopping at the first that works**; all produce a location-independent
35
+ `appknox-mcp` command on PATH (usually `~/.local/bin/appknox-mcp`).
36
+
37
+ > **Always include `--reinstall`** on every `uv tool install` below, even the
38
+ > very first attempt. Without it, `uv` silently does **nothing** if any
39
+ > version of `appknox-mcp` is already installed — including a stale one that
40
+ > predates a flag like `--install-guide` — so re-running "install" can look
41
+ > like it worked while actually leaving old, broken code in place. This is a
42
+ > real failure mode that has happened, not a hypothetical edge case.
43
+
44
+ **a) From PyPI (preferred — try this first).**
45
+ ```bash
46
+ uv tool install --reinstall appknox-mcp
47
+ ```
48
+ Not live yet — while `appknox-mcp` is only on TestPyPI, install from there
49
+ instead (needs `--extra-index-url` since deps like `fastmcp` aren't on
50
+ TestPyPI, and `--index-strategy unsafe-best-match` to let `uv` resolve across
51
+ both indexes):
52
+ ```bash
53
+ uv tool install --reinstall --index https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ --index-strategy unsafe-best-match appknox-mcp
54
+ ```
55
+ Once it's on real PyPI, switch to the plain command above and skip straight to
56
+ verifying — only fall through to (b) if both of these fail.
57
+
58
+ **b) From a released wheel.**
59
+ The repo is **private for now**, so download the wheel with the GitHub CLI (it
60
+ uses the user's existing `gh`/GitHub auth), then install the local file:
61
+
62
+ ```bash
63
+ gh release download --repo appknox/appknox-mcp --pattern '*.whl' --dir /tmp/appknox-mcp
64
+ uv tool install --reinstall /tmp/appknox-mcp/*.whl
65
+ ```
66
+ Add `<tag>` (e.g. `v0.1.0`) as the first arg to `gh release download` to pin a
67
+ version; omit it for the latest. **When the repo is public**, skip `gh` entirely
68
+ and install straight from the asset URL:
69
+ `uv tool install --reinstall "https://github.com/appknox/appknox-mcp/releases/latest/download/appknox_mcp-<version>-py3-none-any.whl"`.
70
+
71
+ **c) From git (no release needed).**
72
+ ```bash
73
+ uv tool install --reinstall "git+ssh://git@github.com/appknox/appknox-mcp@develop" # private: uses the user's SSH key
74
+ # public: uv tool install --reinstall "git+https://github.com/appknox/appknox-mcp@develop"
75
+ ```
76
+
77
+ **d) From a local checkout** (if the user already cloned it):
78
+ `uv tool install --reinstall /path/to/appknox-mcp`.
79
+
80
+ Then verify — `command -v appknox-mcp` only proves *a* binary is on PATH, not
81
+ that it's actually the one you just installed (a stale prior install would
82
+ also pass that check silently, as `--reinstall` above exists specifically to
83
+ prevent). Confirm the real thing instead:
84
+
85
+ ```bash
86
+ appknox-mcp --install-guide | head -1
87
+ ```
88
+
89
+ If that doesn't print `# Installing the Appknox MCP server`, the install
90
+ didn't actually take — re-run step 1's command with `--reinstall` (if you
91
+ skipped it) rather than assuming this step is broken.
92
+
93
+ To update later: `uv tool upgrade appknox-mcp` (works regardless of which
94
+ source it was originally installed from) or re-run the `gh release download` +
95
+ `uv tool install --reinstall` step for a newer wheel.
96
+
97
+ ## 2. Ask the user for credentials
98
+
99
+ Prompt the user for three things (from Appknox dashboard → **Service Accounts**):
100
+
101
+ 1. **Access Key ID**
102
+ 2. **Secret Access Key**
103
+ 3. **Base URL** — the API host for their Appknox instance. Don't assume a
104
+ default: white-labeled deployments use a different host, so ask rather than
105
+ guess (their dashboard has it if they're unsure; Appknox's own KnoxIQ beta
106
+ host is `https://sherlock-mcp.staging.appknox.io`, for reference only).
107
+
108
+ Combine the first two into the token the server expects:
109
+ `APPKNOX_ACCESS_TOKEN = "<Access Key ID>:<Secret Access Key>"` (a single colon
110
+ between them). Hold these in memory for step 3 — never echo the secret back to
111
+ the user or write it anywhere except the client config's `env`.
112
+
113
+ ## 3. Write the MCP config for the client
114
+
115
+ Figure out which client you're configuring (if you're an agent, that's usually
116
+ the client you're running in; otherwise ask). **Merge** the `appknox` entry into
117
+ the existing config — never overwrite other servers. Use `command: "appknox-mcp"`
118
+ with **no args** (the tool install put it on PATH).
119
+
120
+ ### JSON clients — Cursor, Claude Desktop, Windsurf, Claude Code
121
+
122
+ Key is `mcpServers`. Files:
123
+
124
+ | Client | Config file |
125
+ |---|---|
126
+ | Cursor | `~/.cursor/mcp.json` |
127
+ | Claude Desktop (macOS) | `~/Library/Application Support/Claude/claude_desktop_config.json` |
128
+ | Claude Desktop (Linux) | `~/.config/Claude/claude_desktop_config.json` |
129
+ | Claude Desktop (Windows) | `%APPDATA%\Claude\claude_desktop_config.json` |
130
+ | Windsurf | `~/.codeium/windsurf/mcp_config.json` |
131
+ | Claude Code (this project) | `.mcp.json` in the repo |
132
+
133
+ ```json
134
+ {
135
+ "mcpServers": {
136
+ "appknox": {
137
+ "command": "appknox-mcp",
138
+ "env": {
139
+ "APPKNOX_ACCESS_TOKEN": "<Access Key ID>:<Secret Access Key>",
140
+ "APPKNOX_BASE_URL": "<your Appknox base URL>"
141
+ }
142
+ }
143
+ }
144
+ }
145
+ ```
146
+
147
+ ### GitHub Copilot CLI — `~/.copilot/mcp-config.json`
148
+
149
+ Key is `mcpServers` too, but each entry additionally needs `"type": "local"`
150
+ (Copilot CLI's name for a stdio server) and a `"tools"` allowlist — omit
151
+ `"tools"` and the server starts but exposes none of its tools:
152
+
153
+ ```json
154
+ {
155
+ "mcpServers": {
156
+ "appknox": {
157
+ "type": "local",
158
+ "command": "appknox-mcp",
159
+ "args": [],
160
+ "env": {
161
+ "APPKNOX_ACCESS_TOKEN": "<Access Key ID>:<Secret Access Key>",
162
+ "APPKNOX_BASE_URL": "<your Appknox base URL>"
163
+ },
164
+ "tools": ["*"]
165
+ }
166
+ }
167
+ }
168
+ ```
169
+
170
+ The path honors `$COPILOT_HOME` if set (defaults to `~/.copilot`). Verify with
171
+ `/mcp` inside a `copilot` session.
172
+
173
+ > **Note:** this is the standalone **GitHub Copilot CLI**, not Copilot Chat
174
+ > inside VS Code — that one is configured via the **VS Code** section below
175
+ > (VS Code's `.vscode/mcp.json` is shared by both plain VS Code MCP support and
176
+ > Copilot Chat).
177
+
178
+ > **Important — Copilot CLI ignores this server's workflow guidance by
179
+ > default.** Every MCP server can send high-level instructions alongside its
180
+ > tools (ours describes the resolve → triage → fix → verify flow, how to
181
+ > select findings, etc.) — Copilot CLI deliberately does **not** feed these
182
+ > into the model unless you start it with `copilot --allow-all-mcp-server-instructions`
183
+ > (v1.0.66+). Without that flag, Copilot only sees each tool's own
184
+ > name/parameters/docstring, not the overall workflow — it can still call the
185
+ > tools correctly, but won't follow the intended multi-step flow or
186
+ > presentation guidance (e.g. showing exploitability) unless asked explicitly
187
+ > each time. Recommend this flag to anyone using Copilot CLI who wants the
188
+ > guided experience the other clients get by default.
189
+
190
+ ### VS Code — `.vscode/mcp.json`
191
+
192
+ Key is `servers` and the entry needs `"type": "stdio"`:
193
+
194
+ ```json
195
+ {
196
+ "servers": {
197
+ "appknox": {
198
+ "type": "stdio",
199
+ "command": "appknox-mcp",
200
+ "env": { "APPKNOX_ACCESS_TOKEN": "…", "APPKNOX_BASE_URL": "…" }
201
+ }
202
+ }
203
+ }
204
+ ```
205
+
206
+ This is project-scoped (only active in this one repo), unlike every other
207
+ client here — `appknox-mcp --configure vscode` writes the same project-scoped
208
+ file, we don't automate the alternative below. VS Code does have a genuine
209
+ **user-scope** option (works in every workspace): Command Palette → **MCP:
210
+ Open User Configuration**, or `code --add-mcp '{"name":"appknox",...}'` and
211
+ choose **Global** over **Workspace**. VS Code's own docs recommend *against* a
212
+ literal secret even there — use an `${input:...}` variable (`password: true`)
213
+ instead, which prompts once and caches the value in VS Code's own secret
214
+ storage from then on. Set that up by hand if you want VS Code to behave like
215
+ the other clients here; see [VS Code's MCP docs](https://code.visualstudio.com/docs/copilot/customization/mcp-servers)
216
+ for the exact `inputs` array syntax.
217
+
218
+ ### Codex — `~/.codex/config.toml`
219
+
220
+ The env vars **must** go under a nested `[mcp_servers.appknox.env]` table, or
221
+ Codex silently ignores them:
222
+
223
+ ```toml
224
+ [mcp_servers.appknox]
225
+ command = "appknox-mcp"
226
+ args = []
227
+
228
+ [mcp_servers.appknox.env]
229
+ APPKNOX_ACCESS_TOKEN = "<Access Key ID>:<Secret Access Key>"
230
+ APPKNOX_BASE_URL = "<your Appknox base URL>"
231
+ ```
232
+
233
+ > You can skip writing any of the JSON/TOML above by hand and instead let the
234
+ > already-installed `appknox-mcp` command do the merge for you (handles the
235
+ > Codex nesting, Copilot's `type`/`tools` fields, etc. correctly) — works with
236
+ > **no repo clone needed**, since the writer ships inside the package itself:
237
+ > `APPKNOX_ACCESS_TOKEN=<id>:<secret> appknox-mcp --configure <name> --base-url <url>`.
238
+ > To remove an entry later: `appknox-mcp --remove-client <name>`.
239
+
240
+ ### Keep project-scoped tokens out of git
241
+
242
+ `vscode` (`.vscode/mcp.json`) lives in the app repo and now contains the
243
+ token — add it to that repo's `.gitignore`. `claude` no longer needs this: it
244
+ registers at user scope (`~/.claude.json`, outside any repo), not a
245
+ project-scoped file.
246
+
247
+ ### Claude Code only: the plugin (slash commands + fixer agent)
248
+
249
+ > **STOP — agent, check this first:** this section applies ONLY if you are
250
+ > Claude Code. If you are Copilot CLI, Cursor, Codex, VS Code, Windsurf, Claude
251
+ > Desktop, or anything else, **skip this entire section** — do not run
252
+ > `claude plugin ...` commands (the `claude` CLI likely isn't even installed in
253
+ > your environment, and this step is not part of your setup at all) — and
254
+ > continue at "4. Restart and verify" below.
255
+
256
+ The MCP config above gets Claude Code the **tools**, but not `/appknox:triage`,
257
+ `/appknox:fix`, `/appknox:upload`, `/appknox:verify`, or the `appknox-fixer`
258
+ sub-agent — those only load if this repo is installed as a Claude Code
259
+ **plugin**, which is a separate registration.
260
+
261
+ **a) No-clone (preferred — works from a bare `uv tool install`, needs no
262
+ GitHub access at all).** The wheel bundles the plugin's files; this extracts
263
+ them to `~/.appknox-mcp/claude-plugin` and registers that local path:
264
+
265
+ ```bash
266
+ appknox-mcp --install-claude-plugin
267
+ ```
268
+
269
+ **b) From a repo you already have cloned locally**, or the GitHub-hosted form
270
+ (needs the repo on its default branch and — until it's public — doesn't
271
+ reliably work; see below):
272
+
273
+ ```bash
274
+ claude plugin marketplace add /path/to/appknox-mcp # local clone's path
275
+ # or: claude plugin marketplace add appknox/appknox-mcp
276
+ claude plugin install appknox@appknox -y
277
+ ```
278
+
279
+ > **Two separate ways the GitHub-hosted form (b, second line) fails, easy to
280
+ > confuse — (a) and (b)'s local-path form both sidestep both of these:**
281
+ > 1. `.claude-plugin/marketplace.json` must exist **on the repo's default
282
+ > branch** (`develop`, not `main`) — `claude plugin marketplace add
283
+ > owner/repo` always clones that branch, never a feature branch.
284
+ > 2. Even on the default branch, `claude plugin marketplace add owner/repo`
285
+ > does **not** reliably work against a private repo today — it clones via
286
+ > its own internal git (SSH needs a key already loaded in `ssh-agent`;
287
+ > HTTPS fails outright, ignoring `gh`/keychain credentials). See
288
+ > [anthropics/claude-code#17201](https://github.com/anthropics/claude-code/issues/17201).
289
+
290
+ This installs at **user scope** (the default), so the commands/agent are
291
+ available from any repo afterward, not just the one you ran this in. Commands
292
+ are namespaced `/appknox:<name>` (Claude Code's plugin system always prefixes
293
+ `<plugin>:<command>` — there's no way to opt out of that and still use the
294
+ formal plugin mechanism). Its bundled MCP entry reads `APPKNOX_ACCESS_TOKEN`/
295
+ `APPKNOX_BASE_URL` from the environment at launch — export them in the user's
296
+ shell profile, or rely on the project-scoped `.mcp.json` above (token baked in,
297
+ works without exporting anything, but only in that one repo).
298
+ `scripts/install.sh`/`install.ps1` do this step automatically when Claude Code
299
+ is selected.
300
+
301
+ ## 4. Restart and verify
302
+
303
+ MCP servers are spawned when the client starts, so **restart the client** (fully
304
+ quit Claude Desktop with Cmd+Q; restart the Codex or Copilot CLI session; reopen
305
+ Cursor). Then:
306
+
307
+ - Claude Code / Codex / Copilot CLI: run `/mcp` — you should see `appknox` with
308
+ 8 tools.
309
+ - Claude Code only: run `claude plugin list` — you should see `appknox@appknox`
310
+ enabled; try `/appknox:triage` to confirm the slash commands loaded.
311
+ - Ask: *"list the Appknox tools"* — the agent should see `resolve_latest_file`,
312
+ `list_analyses`, `knoxiq_get_fix_plan`, `knoxiq_prepare_fix`,
313
+ `knoxiq_verify_fixes`, etc.
314
+
315
+ ## 5. Update / uninstall
316
+
317
+ ```bash
318
+ uv tool upgrade appknox-mcp # pull the latest server
319
+ uv tool uninstall appknox-mcp # remove the server
320
+ ```
321
+
322
+ Before (or instead of) removing the server itself, remove its entry from each
323
+ client's config: `appknox-mcp --remove-client <client>` — this needs no repo
324
+ clone, since it ships inside the package (see the note in step 3). If the repo
325
+ happens to be cloned, `scripts/uninstall.sh <client>`/`scripts/uninstall.ps1
326
+ <client>` do the same thing and additionally remove the Claude Code plugin
327
+ registration for `claude`. To remove just the plugin by hand:
328
+ `claude plugin uninstall appknox@appknox` then
329
+ `claude plugin marketplace remove appknox`.
@@ -0,0 +1,14 @@
1
+ Copyright (c) 2026 Appknox. All rights reserved.
2
+
3
+ This software and its associated documentation (the "Software") are the
4
+ proprietary and confidential property of Appknox. The Software is licensed, not
5
+ sold, and may be used only in accordance with the terms of a separate written
6
+ agreement with Appknox. Unauthorized copying, distribution, modification, public
7
+ display, or use of the Software, in whole or in part, via any medium, is strictly
8
+ prohibited.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
11
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
12
+ FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL APPKNOX BE
13
+ LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY ARISING FROM, OUT OF, OR IN
14
+ CONNECTION WITH THE SOFTWARE OR ITS USE.