Wiring VS Code to the MCP
With the Kali container running and the MCP server bound to 127.0.0.1:5000,
the next step is to point VS Code at it. Three pieces of configuration —
.vscode/mcp.json, the Copilot agent-mode toggle, and a small loopback
prompt — get you from a running container to a working agent session.
This is the section where the most important governance property of the whole workflow gets switched on: every command the AI proposes appears on screen as a card with an approve and a deny button, and nothing runs until the engineer clicks one of them. That gate is what separates "the AI suggested doing X" from "X happened", and it is the property the rest of the guide assumes is intact. The one operational risk to flag is the option some tools offer to "always approve" a command for the rest of the session — this guide explicitly tells the engineer to leave that turned off, and a sponsor reviewing the workflow should confirm that policy is in place. If the gate is bypassed, the workflow's safety story stops working.
Prerequisites
- VS Code, current stable release.
- The GitHub Copilot and GitHub Copilot Chat extensions, signed in with an account that has Copilot access.
- The Kali MCP lab from Section 04 or 05, running and serving on
localhost:5000. - A folder open in VS Code that will hold the engagement files (authorisation, notes, report). The MCP config sits inside that folder.
Step 1 — Create .vscode/mcp.json
In your engagement folder, create a .vscode subfolder and a
mcp.json inside it. VS Code discovers MCP servers from this file at
workspace scope; keeping the config in the engagement folder means each audit can
have its own list of available tools.
{
"servers": {
"kali-mcp": {
"type": "http",
"url": "http://localhost:5000",
"description": "Kali toolset via mcp-kali-server (Docker, localhost-bound)"
}
}
}
The MCP server inside the container binds to 127.0.0.1:5000. On
Windows and macOS, Docker Desktop's port map exposes that loopback to the host's
loopback only — it does not publish on your LAN interface. This is the
first trust boundary from Section 03: nothing on your network can reach the
MCP server, only software running on the same machine.
Step 2 — Enable agent mode in Copilot Chat
Open the Copilot Chat side panel (the chat-bubble icon in the activity bar). At the bottom of the chat, there is a mode selector — switch it from Ask to Agent. Agent mode is what enables tool-call proposals; without it, Copilot will happily talk about Nmap but cannot run it.
When agent mode loads, VS Code reads .vscode/mcp.json and the MCP
server appears as an attached toolset. You can see the list of available tools in
the chat's Tools picker — Nmap, Hydra, cURL, OpenSSL, and so on, each
exposed as an MCP tool the assistant can propose.
Step 3 — Verify the approval boundary
Before pointing the assistant at any real target, prove to yourself that the approval gate works. Send this prompt in agent mode:
Run a TCP scan of localhost on ports 1-1024 using nmap, default scripts off.
The assistant should propose a tool call that looks roughly like this:
Three things to check before clicking Approve:
- The
targetargument is what you expect. If the assistant has substituted a different address, deny and ask why. - The flags are not doing anything you did not ask for — no
-A(aggressive), no scripts enabled, no UDP. - The target is in scope. For this loopback test, the answer is yes; for a real engagement, the answer is "the authorisation file says so".
VS Code's MCP integration offers an "always approve for this session" option per tool. Do not use it. Every command worth running is worth re-approving — the gate is your last chance to read the proposed arguments against your scope.
What "the approval boundary intact" actually looks like
Through the rest of the guide, you will see the phrase "the approval boundary intact" used about the workflow. Concretely, that means four things hold:
- No tool call leaves the assistant's process without an approve click.
- The target argument of every proposed call is visible to you before approval.
- The flags of every proposed call are visible to you before approval.
- You have time, between the proposal and the approval, to read both against the authorisation file.
If any of those four things is not true — most often because someone enabled auto-approve "just to speed things up" — the workflow has lost its primary safety property, and you are operating outside the boundary the rest of this guide assumes.
Configuration variants
Pointing at a remote lab
If the Kali container is running on a separate workstation on your own network — for example, a homelab box — change the URL to that host's address.
{
"servers": {
"kali-mcp": {
"type": "http",
"url": "http://10.0.0.42:5000",
"description": "Kali MCP on homelab box"
}
}
}
If you do this, the MCP server is no longer loopback-bound and the first trust boundary has changed shape. You need to be confident that the network path between your laptop and the lab is one you control end-to-end. For most readers, the loopback configuration on a single workstation is the right starting point.
Multiple MCP servers
Agent mode can attach more than one MCP server at a time. If you have, for example,
a separate MCP server exposing your ticketing system or your asset inventory, add it
as another entry in the same mcp.json:
{
"servers": {
"kali-mcp": {
"type": "http",
"url": "http://localhost:5000"
},
"assets-mcp": {
"type": "http",
"url": "http://localhost:5100",
"description": "Read-only asset inventory"
}
}
}
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| MCP server does not appear in the Tools picker | mcp.json is in the wrong place, or VS Code has not picked it up. |
Confirm path is .vscode/mcp.json at the workspace root. Reload the window: Ctrl/Cmd + Shift + P → "Developer: Reload Window". |
"Connection refused" against localhost:5000 |
Container is not running, or the MCP service is not started inside it. | docker ps to confirm; docker exec kali-mcp systemctl status kali-mcp-server to verify the service. |
| The assistant does not propose any tool calls | The mode selector is still set to Ask, not Agent. | Switch the mode selector at the bottom of the Chat panel to Agent. |
| Tool call returns immediately with no output | The MCP server is reachable but the underlying tool is not installed in the container. | Shell into the container and confirm: docker exec -it kali-mcp which nmap. If missing, install the metapackage from Section 04 / 05. |
Check yourself
Three questions on keeping the approval gate intact
The whole workflow's safety story rests on the approval gate doing real work on every tool call. These three test whether you would recognise — and refuse — the situations that erode it.
VS Code's MCP integration offers an "always approve for this session" option per tool. Should you ever enable it during a real engagement?
Yes — once you've approved a tool once, repeating the approval adds nothing.
No — every approval is reading the proposed arguments against your scope file, and "always approve" turns that off.
Only for read-only tools like Nmap.
The assistant proposes nmap -A <a-different-host>. You asked it to scan the target named in your authorisation file. What is the single most important thing to do?
Approve the call — the assistant is making a reasonable inference about the environment.
Deny the call, ask the assistant why it substituted that address, and rewrite the prompt if needed.
Approve the call but edit the chat afterwards so the wrong target isn't recorded.
What does Copilot Chat's "agent mode" actually add over plain "ask" mode?