As of April 28, 2026, MCP is no longer just a convenient connector layer for AI coding tools.
It is a permissions surface.
That sounds boring until a repo silently gains a local server that can read files, call APIs, run shell commands, or forward sensitive context to another system.
Then it becomes very exciting in the bad way.
The practical question for a vibe-coded repo is simple.
What should you block before adding one more tool?
This checklist is written for small teams, solo builders, security-minded developers, and anyone who has let an AI coding agent install “just one helpful MCP server” during a late-night build session.
No shame.
That is how half of modern tooling enters a codebase: through convenience with a nice README.
But MCP changes the shape of convenience.
An editor plugin can suggest code.
An MCP server can expose a database, a browser, a repository, a ticket system, a cloud account, or a local command runner.
The difference is not cosmetic.
The difference is where the blast radius starts.
The operating rule
Treat every MCP server as a small integration service, not as a prompt helper.
That means it needs an owner.
It needs a reason to exist.
It needs a permission boundary.
It needs a rollback path.
It needs a place in the repo where future maintainers can see why it was allowed.
If that feels heavy for a tiny side project, good.
That feeling is the useful friction.
MCP is valuable precisely because it lets AI agents reach real systems.
The same property makes casual installs risky.
A vibe-coded repo often has weak project boundaries.
It may have experimental files, copied snippets, personal tokens, local databases, and throwaway scripts in the same workspace.
That is normal during fast building.
It is also exactly why the first MCP security move is not “install a scanner.”
The first move is to decide what the agent must never touch.
Block-before-add table
Use this table before adding any MCP server to a repo.
If a row is unknown, block first and investigate.
| Risk area | Block before adding | Allow only when |
|---|---|---|
| Local command execution | Any server launched by npx, uvx, python, node, bash, or an opaque binary without review |
The command is pinned, reviewed, sandboxed, and owned |
| Secrets | Access to .env, credential stores, SSH keys, cloud config, package tokens, and local keychains |
The server has no read path to secrets or uses a dedicated low-scope token |
| Filesystem | Full home directory or repo-root read/write | The server is limited to specific project paths |
| Network egress | Arbitrary outbound HTTP, webhooks, telemetry, or upload endpoints | Domains are documented and allowlisted |
| Git operations | Tools that can commit, push, rewrite history, initialize repos, or run hooks | A human approves each write operation |
| Browser automation | Login sessions, cookies, admin consoles, and internal dashboards | The session is isolated and the task is read-only or tightly scoped |
| Databases | Write, delete, migrate, export, or unrestricted query tools | Read-only credentials and row/table restrictions are in place |
| Cloud accounts | Admin, billing, IAM, production deploy, and secret-manager access | A break-glass workflow and short-lived credentials exist |
| Issue trackers | Bulk edits, private comments, labels used by automation, and customer data | Tool scopes match the task and audit logs are retained |
| Email and messaging | Send, forward, BCC, export, or search-all-mail tools | The use case is explicit and the token is least privilege |
| Package install | Floating latest installs and unverified packages |
Versions are pinned and provenance is checked |
| Prompt-visible data | Tool output that may contain secrets, private code, or customer text | Output is filtered, truncated, and logged safely |
| Tool metadata | Hidden instructions in descriptions, resources, or prompts | Metadata is reviewed like executable config |
| Auto approval | Auto-run for new or high-risk tools | Only low-risk read tools are auto-approved |
| Shared config | Project-level MCP config committed without review | Changes go through code review |
This table is intentionally conservative.
It is not saying “never use MCP.”
It is saying “do not let the easiest install path define your security boundary.”
That is the whole game.
Why MCP raises the stakes
MCP stands for Model Context Protocol.
The protocol lets clients connect language models to tools, resources, prompts, APIs, and data sources.
Official MCP documentation describes tools as model-controlled capabilities that let models interact with external systems.
That phrase matters.
The model can discover and invoke tools based on context.
The protocol itself does not force one universal approval UI.
Applications are expected to decide how to expose tools, show indicators, and keep humans in the loop.
So your security posture depends on your client, your settings, your server implementation, and your local repo hygiene.
That is a lot of “your.”
For vibe-coded repos, the risk is multiplied by speed.
The faster you add tools, the less likely you are to notice that one tool crosses from read-only helper into actor-with-keys territory.
The correct mental model is:
MCP server equals dependency plus permissions plus runtime process.
It is not just a dependency.
It is not just a chat extension.
It is not just a config entry.
It is all three.
Permission boundary first
Before adding any MCP server, write the permission boundary in plain English.
One sentence is enough.
Example:
This repo may use a GitHub MCP server to read issues and pull requests, but it may not push code, change repository settings, read organization secrets, or access other repositories.
That sentence should drive the config.
If the config cannot express the sentence, the boundary is not enforceable.
If the boundary is not enforceable, the server should stay disabled.
This is where many teams get sloppy.
They define the desired workflow, then accept whatever token or toolset makes the workflow easy.
Flip it.
Define the forbidden actions first.
Then grant only what remains.
A minimum permission policy
Every MCP-enabled repo should have a small policy file or documented section.
It does not need to be fancy.
It needs to be visible.
Use this shape:
## MCP policy
Owner: security-or-repo-owner
Last reviewed: 2026-04-28
Allowed servers:
- github-readonly: read issues, pull requests, code security alerts, and repository metadata for this repo only.
- docs-search: read public documentation only.
Blocked server types:
- local shell command runners
- filesystem servers with home-directory access
- browser automation against authenticated admin consoles
- database servers with write permissions
- any server installed from an unpinned package spec
Default rule:
- New MCP servers are disabled until reviewed.
- New tools are treated as denied until explicitly allowed.
- Secrets are never passed through MCP config, prompts, logs, or tool output.
This is not bureaucracy.
This is future-you leaving a note before present-you gets carried away by a tool demo.
Minimal allowlist template
Here is a vendor-neutral allowlist template you can adapt.
Keep it in repo documentation, security notes, or a reviewed config process.
mcp_policy:
default: deny
review_required_for:
- new_server
- new_tool
- new_scope
- local_process
- network_egress
- write_operation
allowed_servers:
github_readonly:
transport: http
owner: platform-team
purpose: "Read repository context for issues, PRs, and code security signals."
allowed_tools:
- list_issues
- get_issue
- list_pull_requests
- get_pull_request
- list_code_scanning_alerts
- list_secret_scanning_alerts
denied_tools:
- create_or_update_file
- push_files
- create_repository
- delete_file
- update_repository_settings
token_policy:
type: fine_grained
repositories: current_repo_only
permissions: read_only
expiry_days: 30
output_policy:
max_tokens: 10000
redact:
- secrets
- private_keys
- customer_identifiers
docs_search:
transport: http
owner: devrel
purpose: "Search public technical documentation."
allowed_tools:
- search_docs
- fetch_doc
denied_tools:
- execute_code
- access_private_workspace
token_policy:
type: none_or_public
blocked_patterns:
commands:
- "sudo"
- "rm -rf"
- "curl * | sh"
- "bash -c"
- "powershell -enc"
paths:
- ".env"
- ".env.*"
- "~/.ssh/**"
- "~/.aws/**"
- "~/.config/gh/**"
- "secrets/**"
- "config/credentials.*"
domains:
- "unknown-upload-endpoint.example"
logging:
record_tool_name: true
record_arguments: safe_summary_only
record_outputs: redacted
retain_days: 30
This template is deliberately boring.
Boring is excellent when the alternative is “the AI connected five tools and nobody remembers why.”
Claude Code boundary checks
Claude Code documentation is useful because it makes the permission model explicit.
Claude Code settings are hierarchical.
Managed settings can override user and project settings.
Project settings can define permissions and behavior.
The docs also describe permissions.deny for excluding sensitive files such as .env, secret directories, credentials, and build outputs.
Use that idea even outside Claude Code.
The pattern is universal:
deny sensitive paths first.
allow tools second.
Then decide where sandboxing fits.
Claude Code permission docs also separate permissions from sandboxing.
Permissions decide which tools, files, or domains the assistant can access.
Sandboxing provides operating-system-level enforcement for shell commands and child processes.
Those are different controls.
You want both when local execution is involved.
A permission prompt is not a sandbox.
A sandbox is not a review process.
A deny rule is not a secret-management strategy.
Security improves when those layers overlap.
GitHub MCP boundary checks
GitHub’s MCP and Copilot docs are also a good signal for where enterprise tooling is heading.
GitHub documents organization and enterprise controls for MCP usage.
Companies can allow or block MCP server usage.
They can restrict access to servers listed in an MCP registry.
They can provide a curated catalog so developers do not install random servers from a search result.
For cloud agent usage, GitHub’s docs show a read-only GitHub MCP endpoint pattern.
They also describe using toolsets and individual tools to narrow what the agent can do.
That is the right instinct.
Do not treat “GitHub access” as one permission.
Break it down.
Read issues is different from write code.
Read pull requests is different from merge pull requests.
List code scanning alerts is different from changing branch protection.
Read secret scanning alerts is different from bypassing push protection.
If you use a personal access token, prefer fine-grained tokens.
Limit repositories.
Limit permissions.
Set expiration.
Rotate when the MCP server changes.
And never let the token become a general-purpose “AI can do GitHub now” pass.
That pass will be abused eventually, even if only by accident.
Local STDIO servers need extra suspicion
Local STDIO servers are attractive because they are easy.
Run a package.
Start a local process.
Expose tools to the agent.
Done.
The official MCP security best-practices document calls out local server compromise directly.
Local MCP servers may run on the user’s machine and have direct access to the user’s system.
The same guidance warns about malicious startup commands, malicious payloads inside servers, insecure local HTTP servers, arbitrary code execution, and data exfiltration.
That is not theoretical enough to ignore.
A local server launched with your user account inherits your local mess.
Your SSH keys.
Your package tokens.
Your cloud credentials.
Your checked-out private repos.
Your browser-adjacent files.
Your temp directories full of “I will clean this later” artifacts.
The server does not need to be evil to be dangerous.
It can simply be careless.
Local server review checklist
Before allowing a local MCP server, answer these questions.
- What exact command starts it?
- Is the command shown in full, including arguments?
- Is the package version pinned?
- Is the package name resistant to typosquatting?
- Is the publisher the expected vendor or maintainer?
- Is the source repository linked from the official provider page?
- Does the server execute shell commands?
- Does it use
child_process.exec,shell=True,eval, or string-built commands? - Does it read environment variables wholesale?
- Does it log arguments or outputs that may contain secrets?
- Does it phone home?
- Does it open a localhost HTTP port?
- If it opens a port, does it require authorization?
- Can another local process reach it?
- Can a browser page reach it through DNS rebinding or localhost tricks?
- Does it access the home directory?
- Does it access the repo root?
- Can it write files?
- Can it delete files?
- Can it run package managers?
- Can it run Git hooks?
- Can it start other processes?
- Is there a sandbox?
- Is there a kill switch?
- Is there an owner?
- Is there a reason this cannot be done with a narrower remote API?
If the answer is “we do not know” more than twice, block it.
That is not paranoia.
That is basic dependency hygiene with a sharper object.
Package and server vetting
MCP server vetting should feel closer to dependency review than plugin browsing.
Start with provenance.
Find the canonical project page.
Find the package registry page.
Find the source repository.
Check whether those three agree.
If the docs say the server is official, the install command should point to an official namespace or a clearly maintained repository.
If a marketplace lists it, confirm whether the marketplace is curated or just a directory.
If a blog post recommends it, treat that as demand signal only.
Then check release behavior.
Look for recent unexplained ownership changes.
Look for brand-new packages with popular names.
Look for packages that install postinstall scripts without a clear need.
Look for binaries fetched from random URLs.
Look for install instructions that pipe network output into a shell.
Look for “latest” in production docs.
Look for broad token instructions like “create a classic PAT with repo scope.”
Look for environment examples that put secrets directly into shared config.
Look for tool lists that include admin functions you do not need.
Then review the server’s dangerous edges.
Command execution.
Filesystem access.
Network upload.
Token handling.
Logging.
Prompt-visible metadata.
Tool descriptions.
Resource descriptions.
Examples that encourage auto-run.
This review is not about reading every line of code.
It is about finding the places where a small bug becomes repo compromise.
Supply-chain traps to block
Block floating package installs for MCP servers.
npx some-server@latest is convenient.
It is also a moving executable boundary.
Prefer a pinned version.
Prefer lockfiles where possible.
Prefer checksums or signed releases for binaries.
Prefer official registries over copy-pasted install snippets.
Prefer a maintained source repository with clear security reporting.
Prefer servers that publish minimal scopes and documented toolsets.
Avoid packages that mimic official names.
Avoid abandoned packages with sensitive capabilities.
Avoid servers that require broad user tokens for simple read tasks.
Avoid servers that ask for cloud admin when the task is documentation lookup.
Avoid any install flow that hides the real command.
The MCP best-practices guidance says consent screens should show exact local commands before execution.
That is a strong product requirement.
You can turn it into a repo requirement:
no exact command, no install.
Prompt injection is a tool problem now
Prompt injection used to sound like a chat problem.
With tools, it becomes an operations problem.
An untrusted webpage can tell a browser tool to ignore prior instructions.
A malicious issue comment can ask an agent to exfiltrate repository files.
A commit message can contain instructions that look important to a model.
A tool description can smuggle hidden priorities into the model context.
A resource returned by one server can influence calls to another server.
That is the uncomfortable part.
The attacker may not need to compromise the MCP server.
They may only need to place text where the model will read it.
The GitHub Advisory Database entry for @cyanheads/git-mcp-server is a useful example of the shape.
The advisory describes command injection in MCP tools and also discusses indirect prompt injection through Git logs as a realistic route to unintended tool execution.
The fix category is familiar: do not build shell commands with untrusted input.
But the agentic path is newer: the model reads hostile text, then chooses a tool path.
So the checklist must cover both code safety and context safety.
Data exfiltration paths
Data exfiltration does not require a cartoon villain server.
It can happen through normal-looking tool output.
Examples:
- a filesystem tool reads
.envand includes it in the chat context - a GitHub tool retrieves private issue details and passes them into another tool
- a browser tool reads an internal admin page and summarizes customer data
- a database tool exports rows into a prompt-visible response
- a docs tool follows a URL to an attacker-controlled page
- a logging layer stores full tool arguments with secrets
- a remote MCP server receives headers or tokens it does not need
- a local server sends telemetry containing file paths or snippets
The defense is not one magic setting.
Use several small controls.
Block sensitive paths.
Use read-only tokens.
Limit tool output size.
Redact secrets before output enters the model context.
Avoid chaining untrusted read tools into write tools.
Do not pass credentials through prompts.
Do not store secrets in shared MCP config.
Do not let one server become the bridge between private data and arbitrary network egress.
Tool chaining rules
The riskiest MCP behavior often appears between tools.
One server reads.
Another server writes.
A third server sends.
The model becomes the router.
That can be useful.
It can also create an exfiltration lane.
Define chaining rules:
- Public docs may feed code suggestions.
- Public docs may not feed credential changes.
- Browser snapshots may not trigger shell commands.
- Issue comments may not trigger file deletion.
- Git logs may not trigger repository initialization.
- Database query results may not be sent to messaging tools.
- Secret scanning results may not be pasted into third-party tools.
- Production observability data may not be exported without approval.
- Any untrusted text source must be treated as data, not instruction.
That last line should be written somewhere visible.
Untrusted text is data.
Not instruction.
Print it on a sticky note if necessary.
Developer security sometimes needs office supplies.
Auto-run policy
Auto-run is where vibe coding gets spicy.
Fast iteration rewards fewer prompts.
Security rewards more boundaries.
You need a middle path.
Allow auto-run only for low-risk read tools.
Examples that may qualify:
- search public docs
- fetch public docs
- list local project files outside denied paths
- read repository metadata
- list non-sensitive issues
- inspect dependency versions
Require confirmation for medium-risk tools.
Examples:
- run tests
- edit files
- create branches
- query private systems
- fetch private tickets
- read logs
- access browser sessions
Block or require elevated approval for high-risk tools.
Examples:
- delete files
- push commits
- publish packages
- rotate secrets
- change IAM
- deploy production
- run arbitrary shell
- execute database writes
- send messages externally
- export customer data
If your client has permission modes, define which mode is acceptable per repo.
Do not let individual developers silently turn on the most permissive mode because a demo was annoying.
Annoyance is not a threat model.
It is just a very persuasive coworker.
Secrets handling
Secrets should not live in MCP config.
They should not be pasted into prompts.
They should not appear in tool descriptions.
They should not appear in example commands committed to the repo.
They should not be logged as MCP arguments.
They should not be returned in tool output.
For local development, use environment injection only when the server truly needs it.
Prefer dedicated low-scope credentials.
Prefer short-lived credentials.
Prefer read-only credentials.
Prefer per-repo or per-environment credentials.
Prefer OAuth flows that do not expose third-party credentials to the client.
For GitHub, prefer fine-grained PATs or built-in scoped tokens where the platform provides them.
For cloud providers, prefer role-based short-lived access over static admin keys.
For databases, create a separate read-only user for MCP exploration.
For SaaS tools, disable write scopes unless the workflow demands them.
If a server cannot operate without a broad token, the server may be too broad for the repo.
Filesystem boundaries
Filesystem access deserves its own review because vibe-coded repos are messy by design.
Add explicit deny paths.
At minimum:
{
"permissions": {
"deny": [
"Read(./.env)",
"Read(./.env.*)",
"Read(./secrets/**)",
"Read(./config/credentials.json)",
"Read(../**/.env)",
"Read(~/.ssh/**)",
"Read(~/.aws/**)",
"Read(~/.config/gh/**)"
]
}
}
Adapt the syntax to your client.
The principle is what matters.
Deny secrets before you debate convenience.
Then restrict reads.
Then restrict writes.
Then restrict execution.
If a filesystem MCP server needs the whole home directory, ask why.
If the answer is “so the agent can find things,” block it.
Search convenience is not a reason to expose your whole machine.
Network boundaries
Network egress is the quiet part of MCP security.
A server with read access and arbitrary outbound network access can leak data.
The leak can look like analytics.
It can look like error reporting.
It can look like a webhook.
It can look like “sync.”
Document allowed domains.
Block unknown upload endpoints.
Block pastebin-style destinations.
Block arbitrary webhooks.
Block direct calls to unfamiliar IPs.
Block telemetry that includes prompts, code snippets, filenames, tokens, or customer data.
For remote MCP servers, document the base URL.
For local MCP servers, document outbound domains.
For browser tools, isolate sessions.
For cloud agents, use provider firewall controls where available.
The point is not to make networking impossible.
The point is to make unexpected networking visible.
Auth and OAuth checks
The current MCP authorization docs lean on OAuth 2.1 patterns for HTTP-based transports.
Important requirements and recommendations include protected resource metadata, authorization server discovery, PKCE, HTTPS, redirect URI validation, token audience binding, and secure token storage.
You do not need to memorize every RFC number to use the checklist well.
Ask these practical questions:
- Does the server require auth for sensitive tools?
- Are tokens short-lived?
- Are refresh tokens protected?
- Is PKCE used where required?
- Are redirect URIs exact and limited?
- Are localhost redirect risks understood?
- Does the server validate token audience?
- Does the server reject tokens meant for another resource?
- Does the server avoid token passthrough to downstream APIs?
- Are scopes minimal at first connection?
- Are privileged scopes requested only when needed?
- Does the consent screen show the actual client and scopes?
- Are third-party authorization flows clearly separated?
If you operate an MCP server, these are implementation requirements.
If you consume an MCP server, these are vendor questions.
If the vendor cannot answer, do not grant production access.
Server implementation checks
For teams writing their own MCP servers, review the server like a small API gateway.
Validate all inputs.
Use schemas.
Reject unknown fields.
Avoid shell interpretation.
Use argument arrays instead of command strings.
Escape is not a strategy by itself.
Canonicalize paths.
Prevent path traversal.
Limit file roots.
Separate read tools from write tools.
Separate admin tools from normal tools.
Require auth for private data.
Validate token audience.
Log security events.
Redact sensitive data.
Return minimal errors.
Limit output size.
Limit concurrency.
Set timeouts.
Avoid long-running local processes with broad access.
Publish a security policy.
Publish supported toolsets.
Publish required scopes.
Publish a changelog that calls out permission changes.
When a tool gains a new capability, treat it like a permission migration.
Do not hide it in a patch release.
Repo preflight before adding MCP
Run this preflight in the repo before adding an MCP server.
- Search for secrets committed by accident.
- Move real secrets out of the workspace.
- Add or update secret-deny patterns.
- Decide whether MCP config is user-only, project-only, or managed.
- Decide who approves MCP config changes.
- List allowed servers.
- List blocked server categories.
- List denied paths.
- List allowed domains.
- List allowed tools.
- List denied tools.
- List token scopes.
- Set token expirations.
- Decide whether auto-run is allowed.
- Decide whether local STDIO servers are allowed.
- Decide whether browser automation is allowed.
- Decide whether production systems are reachable.
- Decide whether private customer data is reachable.
- Decide where tool logs are stored.
- Decide how logs are redacted.
- Decide how to disable all MCP servers quickly.
- Add a review date.
This is the “before” step.
Do it before the first install.
Doing it after ten installs is still useful, but now you are archaeology-ing your own repo.
Nobody enjoys security archaeology.
The shovel is always made of regret.
Review workflow for pull requests
If MCP config is committed to the repo, treat changes as security-relevant.
Require review for:
- new MCP server entries
- changed commands
- changed arguments
- changed package versions
- changed URLs
- changed headers
- changed environment variables
- new tools
- broader scopes
- new write tools
- new network domains
- new filesystem paths
- removed deny rules
- enabled auto approval
- disabled sandboxing
A reviewer should ask:
- What does this server do?
- Why is it needed?
- What can it read?
- What can it write?
- What can it execute?
- What can it send over the network?
- Which token does it use?
- What happens if the server is malicious?
- What happens if the server is buggy?
- How do we turn it off?
If the PR description cannot answer those questions, the PR is not ready.
Incident response plan
Small repos need incident plans too.
Keep it simple.
If an MCP server is suspected of leaking data or executing unintended actions:
- Disable the server.
- Stop the local process or remove the remote config.
- Revoke its tokens.
- Rotate any credentials it could read.
- Check shell history and MCP logs.
- Check Git history for unexpected commits.
- Check package-lock or lockfile changes.
- Check network logs where available.
- Check cloud audit logs.
- Check SaaS audit logs.
- Check whether prompt-visible output included secrets.
- Remove malicious or unnecessary config.
- Patch or pin the server.
- Document what happened.
- Add a deny rule so the same path is blocked next time.
Do not begin with blame.
Begin with containment.
The human who clicked “approve” probably did what the tool design encouraged.
Fix the design.
What to allow first
Start with read-only, low-blast-radius servers.
Good first candidates:
- public documentation search
- repository issue read access
- pull request read access
- dependency metadata lookup
- code scanning alert read access
- secret scanning alert read access
- local file read access limited to a safe docs folder
Delay higher-risk servers:
- shell command servers
- unrestricted filesystem servers
- browser automation with logged-in sessions
- database write servers
- cloud deployment servers
- package publishing servers
- email sending servers
- messaging servers
- production incident tools
This staged approach helps teams learn the operational behavior of MCP without handing the agent a loaded toolbox on day one.
There is no prize for connecting everything first.
There is only a larger cleanup later.
Vibe-coded repo example
Imagine a small SaaS repo built mostly with AI assistance.
It has:
- a Next.js app
- a local SQLite database
- a
.env.localfile - Stripe test keys
- a GitHub repo
- Linear issues
- Vercel deployments
- a few scripts generated during experiments
- a half-finished admin page
The developer wants to add GitHub, Linear, Playwright, and filesystem MCP servers.
The unsafe version is:
Add all four, enable auto-run, and let the agent figure it out.
The safer version is:
- GitHub read-only for issues and PRs.
- Linear read-only for current project issues.
- Public docs search allowed.
- Filesystem read limited to
docs/and selected source folders. .env*,secrets/, local database files, and cloud config denied.- Playwright disabled for authenticated admin pages.
- Vercel deploy tools blocked until manual release workflow exists.
- Auto-run allowed only for public docs search.
- Any write tool requires explicit approval.
This still feels fast.
It just stops fast from becoming slippery.
Common mistakes
Mistake one: treating MCP config as personal preference.
If the config affects a repo, it affects everyone who trusts that repo.
Mistake two: allowing tool categories instead of tools.
“GitHub tools” is too broad.
Name the tools.
Mistake three: using one broad token for many tasks.
One token should not read issues, push code, manage secrets, and deploy production.
Mistake four: trusting tool descriptions.
Tool descriptions are model context.
Review them as instruction-bearing text.
Mistake five: assuming read-only means harmless.
Read-only plus outbound network can still leak data.
Mistake six: assuming prompts can enforce boundaries.
Prompts help behavior.
Permissions enforce limits.
Mistake seven: ignoring local processes.
A local MCP server is still executable code on your machine.
Mistake eight: skipping logs.
If you cannot reconstruct tool calls, you cannot debug incidents.
Mistake nine: allowing “latest” forever.
Permission drift can arrive as a dependency update.
Mistake ten: adding security after the repo becomes important.
Repos become important gradually, then suddenly.
Very annoying of them.
The practical checklist
Use this as the working checklist.
- Define the repo’s MCP owner.
- Define the reason for each allowed server.
- Set default to deny.
- Add sensitive path deny rules.
- Block
.envand.env.*. - Block SSH keys.
- Block cloud credential directories.
- Block package registry tokens.
- Block local database files unless explicitly needed.
- Prefer remote HTTP servers with proper auth for SaaS tools.
- Treat local STDIO servers as executable dependencies.
- Show and review exact startup commands.
- Pin package versions.
- Verify package publisher.
- Verify source repository.
- Check for recent advisories.
- Check for command execution.
- Check for filesystem access.
- Check for network egress.
- Check for telemetry.
- Check for logging behavior.
- Prefer read-only tokens.
- Prefer fine-grained tokens.
- Prefer current-repo-only access.
- Prefer short token expiration.
- Avoid broad classic tokens.
- Avoid admin scopes.
- Avoid shared personal tokens.
- Avoid secrets in MCP config.
- Avoid secrets in prompts.
- Avoid secrets in logs.
- List allowed tools by name.
- Deny write tools by default.
- Deny shell tools by default.
- Deny deploy tools by default.
- Deny email send tools by default.
- Deny database write tools by default.
- Deny package publish tools by default.
- Limit tool output size.
- Redact sensitive output.
- Treat untrusted text as data.
- Do not let issue comments trigger writes.
- Do not let Git logs trigger shell commands.
- Do not let browser pages trigger secret reads.
- Do not chain private read tools into external send tools.
- Require confirmation for file edits.
- Require confirmation for Git writes.
- Require confirmation for browser actions on authenticated pages.
- Require elevated approval for production access.
- Keep audit logs.
- Review MCP config in pull requests.
- Rotate tokens after server changes.
- Disable unused servers.
- Re-review allowed servers monthly or after major updates.
This checklist is long because “add tool” is no longer a tiny action.
It is a trust decision.
FAQ
Is MCP unsafe by default?
No.
MCP is a protocol for connecting models to tools and data sources.
The risk comes from what a specific server can access, how the client approves tool use, how credentials are scoped, and how much untrusted context the model sees.
Should I ban all local MCP servers?
For production or company repos, blocking local servers by default is reasonable.
Then allow exceptions when the exact command, package, version, filesystem access, network access, and sandbox are reviewed.
For personal experiments, at least avoid broad home-directory access and floating package installs.
Is read-only MCP access enough?
Read-only is safer than write access, but it is not automatically safe.
Read-only access can still expose private code, tickets, logs, customer records, or secrets if outputs are sent somewhere else.
Pair read-only access with egress limits and output redaction.
Should MCP config be committed?
Project-level config can be useful because it makes tool access reviewable.
But committed config should avoid secrets and should go through code review.
User-only config is better for personal experiments that should not become team defaults.
Managed settings are better for organizations that need central enforcement.
What is the safest first MCP server?
A public documentation search server with no credentials and no write tools is usually a safer first step.
After that, add read-only repository context with scoped credentials.
Do not start with shell, browser, cloud admin, or database write tools.
What should I do if an agent already added servers?
Inventory them.
Disable unknown servers.
Review commands, packages, URLs, tools, tokens, and logs.
Revoke tokens you cannot explain.
Then rebuild the allowlist from scratch.
Can prompts prevent prompt injection?
Prompts help, but they are not enough.
Prompt injection becomes more dangerous when tools can act on the injected text.
Use permissions, confirmation prompts, output filtering, and tool chaining rules.
Why focus on vibe-coded repos?
Because vibe-coded repos often grow faster than their security model.
They are full of useful experiments, half-finished scripts, and local context that an agent can accidentally touch.
The checklist creates a boundary before the repo becomes too useful to casually clean up.
Sources
- Model Context Protocol, “Security Best Practices”
- Model Context Protocol, “Tools”
- Model Context Protocol, draft “Authorization”
- Model Context Protocol, “Understanding Authorization in MCP”
- Anthropic Claude Code, “Settings”
- Anthropic Claude Code, “Connect Claude Code to tools via MCP”
- Anthropic Claude Code, “Configure permissions”
- Anthropic Claude Code, “Security”
- GitHub Docs, “MCP server usage in your company”
- GitHub Docs, “Configure MCP server access for your organization or enterprise”
- GitHub Docs, “Adding MCP servers for GitHub Copilot CLI”
- GitHub Docs, “Connect agents to external tools”
- GitHub Advisory Database, “CVE-2025-53107: @cyanheads/git-mcp-server vulnerable to command injection in several tools”
- Cursor Security policy and advisories index