QA How-To
MCP Security Testing Complete Guide (2026)
Use this mcp security testing complete guide 2026 to test prompt injection, permissions, schemas, secret leakage, and secure MCP server behavior safely.
22 min read | 3,670 words
TL;DR
Secure MCP testing combines protocol validation, prompt injection probes, permission boundary tests, JSON Schema fuzzing, and secret leakage detection. Build deterministic tests around the MCP client-server boundary, then add model-driven evaluations for attacks that depend on agent behavior.
Key Takeaways
- Test the MCP trust boundary at protocol, tool, data, and model integration layers.
- Treat tool descriptions, resource content, and tool results as untrusted input.
- Verify deny-by-default authorization with negative tests for every sensitive tool.
- Generate malformed and boundary arguments from each tool's JSON Schema.
- Scan errors, logs, resources, and tool results for secrets and sensitive data.
- Run deterministic protocol tests before slower model-driven adversarial evaluations.
- Record security decisions and evidence so failures are reproducible and auditable.
The mcp security testing complete guide 2026 gives QA and SDET engineers a practical way to test Model Context Protocol servers before an AI agent can misuse their tools, resources, or data. You will build a TypeScript test harness that connects to a local MCP server, inventories its exposed capabilities, sends hostile inputs, verifies authorization, fuzzes arguments, and scans every response for secrets.
MCP consistency does not remove risk. A server can expose destructive tools, trust hostile document instructions, accept ambiguous arguments, or leak credentials. The client, server, transport, tool implementation, identity system, and upstream APIs all contribute to the security boundary.
This pillar surveys the full workflow. Use the linked tutorials for deeper attack payloads, permission matrices, fuzz generators, and leakage scanners.
TL;DR
| Security area | What to test | Passing evidence |
|---|---|---|
| Capability exposure | Listed tools and resources match policy | Approved inventory diff |
| Prompt injection | Untrusted content cannot redirect privileged actions | No unauthorized tool call |
| Permissions | Identity, scope, tenant, and confirmation checks | Explicit denial with stable error |
| Arguments | Types, bounds, formats, extra fields, and payload size | Invalid input rejected before side effects |
| Secret leakage | Results, errors, logs, resources, and metadata | Scanner finds no credential patterns |
| Transport and sessions | Authentication, origin, session isolation, timeouts | Cross-session and unauthenticated requests fail |
Start with deterministic tests that call MCP operations directly. They are fast, reproducible, and easy to debug. Add model-driven attack simulations after the server boundary is proven, because a model can reveal instruction-following failures that a protocol-only suite cannot observe.
What You Will Build
By the end of this guide, you will have:
- A Node.js and TypeScript harness using the official MCP SDK over standard input and output.
- A capability inventory that fails when unexpected tools appear.
- Negative tests for unauthorized or malformed tool calls.
- A lightweight argument fuzzer driven by tool schemas.
- A recursive secret scanner for results and errors.
- A layered test plan for prompt injection, transport, sessions, and production evidence.
The sample local server exposes read and delete tools. Replace its test token with your authorization adapter and test identities.
Prerequisites
Use Node.js 20 or newer and npm. The examples use TypeScript, Vitest, and the official @modelcontextprotocol/sdk package. Pin the installed versions in your lockfile and review SDK release notes before upgrading.
mkdir mcp-security-lab
cd mcp-security-lab
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install --save-dev typescript tsx vitest @types/node
npx tsc --init --module nodenext --moduleResolution nodenext --target es2022
mkdir -p src test
Add scripts and ESM mode to package.json:
{
"type": "module",
"scripts": {
"test": "vitest run",
"server": "tsx src/server.ts"
}
}
Use only synthetic records and fake credentials in this lab. Never point destructive security tests at production. If you need a broader automation foundation, review API testing strategy for QA engineers and AI testing fundamentals before adding MCP-specific cases.
Step 1: Map the MCP Security Testing Complete Guide 2026 Threat Model
Begin with assets, actors, entry points, and forbidden outcomes. Assets include credentials, customer records, tool authority, conversation context, resource contents, and audit logs. Actors include the end user, MCP client, model, server, tool owner, upstream API, and an attacker who controls a prompt, file, web page, tool result, or remote server.
Draw trust boundaries around the model host, MCP client, transport, server process, and each downstream service. For every boundary, ask what identity crosses it, what data is trusted, and what operation can cause a side effect. A tool call is not authorized merely because the model requested it. The server must make its own decision using authenticated identity and server-side policy.
Classify tests by layer:
| Layer | Representative failure | Test style |
|---|---|---|
| Protocol | Invalid request crashes the server | Direct client request |
| Description and content | Malicious text persuades the agent | Model-driven evaluation |
| Tool authorization | User reads another tenant | Identity matrix |
| Tool implementation | Shell or SQL injection | Unit and integration test |
| Transport | Session or token is reused | HTTP or process test |
| Operations | Secret enters logs | Log and telemetry scan |
Create a short policy file for your actual server. List approved tool names, required roles, allowed tenants, whether confirmation is required, maximum argument sizes, and prohibited response fields. This policy becomes the oracle for tests.
Verification: Review the map with the server owner and security owner. Every sensitive tool must have an owner, an authorization rule, and at least one forbidden outcome that can become a negative test. If a reviewer cannot state who may call a tool and under what conditions, the threat model is incomplete.
Step 2: Create a Secure Reference MCP Server
Create src/server.ts. This sample uses McpServer, registers tools with Zod schemas, and connects through StdioServerTransport. The delete operation checks authorization before changing state and returns a controlled tool error rather than throwing internal details.
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
const server = new McpServer({ name: 'security-lab', version: '1.0.0' });
const records = new Map([['rec-1', { id: 'rec-1', owner: 'tenant-a' }]]);
server.registerTool(
'read_record',
{
description: 'Read one synthetic record by identifier',
inputSchema: { id: z.string().regex(/^rec-[0-9]+$/) }
},
async ({ id }) => {
const record = records.get(id);
if (!record) {
return { content: [{ type: 'text', text: 'Not found' }], isError: true };
}
return { content: [{ type: 'text', text: JSON.stringify(record) }] };
}
);
server.registerTool(
'delete_record',
{
description: 'Delete a synthetic record after server-side authorization',
inputSchema: {
id: z.string().regex(/^rec-[0-9]+$/),
authorization: z.string().min(1)
}
},
async ({ id, authorization }) => {
if (authorization !== 'test-delete-token') {
return {
content: [{ type: 'text', text: 'Forbidden' }],
isError: true
};
}
const deleted = records.delete(id);
return { content: [{ type: 'text', text: JSON.stringify({ deleted }) }] };
}
);
await server.connect(new StdioServerTransport());
This is a teaching server, not a production authentication design. In production, do not accept authority supplied as an ordinary model-generated argument. Bind the tool request to an authenticated transport identity or trusted server session, then enforce scope and tenant rules inside the server. Keep credentials outside tool descriptions and model-visible context.
Run it once:
npm run server
A stdio MCP server waits silently for framed client messages, so silence is normal. Stop it with Ctrl+C.
Verification: The process starts without a module or schema error and remains running. Confirm that startup writes no banner to standard output, because stdout is reserved for MCP protocol messages. Diagnostic messages should go to stderr.
Step 3: Inventory Tools and Enforce Least Privilege
Create test/security.test.ts with a reusable client lifecycle. The test launches the server as a child process, lists tools, and compares names with an approved inventory. An accidental admin tool now fails the build.
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
const client = new Client({ name: 'security-tests', version: '1.0.0' });
let transport: StdioClientTransport;
beforeAll(async () => {
transport = new StdioClientTransport({
command: process.execPath,
args: ['--import', 'tsx', 'src/server.ts'],
stderr: 'pipe'
});
await client.connect(transport);
});
afterAll(async () => {
await client.close();
});
describe('MCP capability policy', () => {
it('exposes only approved tools', async () => {
const response = await client.listTools();
const names = response.tools.map((tool) => tool.name).sort();
expect(names).toEqual(['delete_record', 'read_record']);
});
it('publishes an input schema for every tool', async () => {
const response = await client.listTools();
for (const tool of response.tools) {
expect(tool.inputSchema.type).toBe('object');
expect(tool.inputSchema).toHaveProperty('properties');
}
});
});
Listing tests are necessary but not sufficient. A client can hide a tool in its UI while the server still exposes it. Test the server response directly. Also inventory resources, prompts, and resource templates if your server supports them. Snapshot review should focus on security-relevant changes instead of approving a large generated snapshot without inspection.
For each sensitive capability, build a subject, action, resource, environment matrix. Include anonymous, normal user, privileged user, wrong tenant, expired session, revoked role, missing scope, and approval-not-granted cases. The expected default is deny. See the deeper MCP tool permission boundary tutorial for a complete matrix.
Verification: Run npm test. Both inventory tests should pass. Temporarily register a tool named debug_dump and rerun the suite. The approved-name assertion must fail, proving that the guard detects capability drift. Remove the temporary tool afterward.
Step 4: Test Permission Boundaries and Side Effects
Add direct call tests inside the same describe block. These assertions check the returned isError signal and verify that a denied delete did not change the record. The follow-up read is important because an error response alone does not prove absence of a side effect.
it('denies deletion without authority and preserves the record', async () => {
const denied = await client.callTool({
name: 'delete_record',
arguments: { id: 'rec-1', authorization: 'wrong-token' }
});
expect(denied.isError).toBe(true);
expect(JSON.stringify(denied.content)).toContain('Forbidden');
const read = await client.callTool({
name: 'read_record',
arguments: { id: 'rec-1' }
});
expect(read.isError).not.toBe(true);
expect(JSON.stringify(read.content)).toContain('tenant-a');
});
it('allows the explicitly authorized deletion', async () => {
const result = await client.callTool({
name: 'delete_record',
arguments: { id: 'rec-1', authorization: 'test-delete-token' }
});
expect(result.isError).not.toBe(true);
expect(JSON.stringify(result.content)).toContain('\"deleted\":true');
});
Order-dependent tests are fragile. The example is intentionally linear for learning, but a production suite should create a fresh record or fresh server for each test. Assert the upstream state, database transaction, queue, or audit event after every denied operation. Test both horizontal escalation, such as tenant A reading tenant B, and vertical escalation, such as a reader invoking an administrator tool.
Consent is separate from authorization. A user may have permission to send an email but still need to approve recipients and content for a specific call. For consequential actions, verify that the server or trusted client displays the exact action, binds approval to immutable arguments, rejects stale approval, and prevents replay.
Verification: Run npm test. The denied call must return isError: true, and the read immediately afterward must find rec-1. The authorized call must report deleted: true. If only the response assertion passes, inspect the actual backing store before accepting the control.
Step 5: Fuzz Tool Arguments From JSON Schema
Schema validation is the first filter against malformed calls, type confusion, parser differentials, excessive payloads, and unexpected properties. It does not replace business validation. A syntactically valid identifier may still reference another tenant, and a valid string may contain a dangerous shell fragment if the implementation concatenates commands.
Add a compact negative corpus to the test file:
const invalidReadArguments: unknown[] = [
{},
{ id: null },
{ id: 7 },
{ id: '' },
{ id: 'record-1' },
{ id: 'rec-1', unexpected: true },
{ id: `rec-${'9'.repeat(50_000)}` }
];
it.each(invalidReadArguments)(
'rejects malformed read_record arguments: %j',
async (argumentsValue) => {
const result = await client.callTool({
name: 'read_record',
arguments: argumentsValue as Record<string, unknown>
});
expect(result.isError).toBe(true);
}
);
Depending on server and SDK schema behavior, unknown fields might be stripped rather than rejected. Decide and document your policy. For security-sensitive tools, explicit rejection often gives clearer evidence and prevents clients from believing an ignored control was honored. Configure schemas accordingly and test the observed behavior.
Expand generation by schema keyword: omit each required field, cross types, test empty and Unicode strings, use values just below and above minimum and maximum, violate patterns and formats, add nested objects, duplicate semantic identifiers, and cap payload size before parsing. Preserve every failing seed so the issue becomes a deterministic regression. The JSON Schema MCP argument fuzzing guide builds this into a reusable generator.
Verification: Run npm test. Every corpus row must produce a controlled tool error and the server must remain available for a valid call afterward. Add that final health call in production suites to detect crashes, hangs, or corrupted state. Monitor memory and execution time when testing large payloads.
Step 6: Probe Prompt Injection Across MCP Content
Prompt injection testing asks whether untrusted text can alter the agent's instructions or trigger a privileged action. Attack text can arrive through a user message, resource, tool result, prompt template, file, issue description, database row, or web page. Tool metadata itself is also influential because descriptions tell the model when and how to call a tool.
Use two complementary test layers. First, protocol tests ensure the server labels content correctly, minimizes returned data, and never performs a second action merely because an argument contains instructions. Second, model-driven evaluations run a real agent in an isolated environment and observe attempted calls. Include direct instructions, indirect instructions embedded in retrieved content, obfuscated text, multilingual variants, authority claims, fake system messages, and requests to reveal hidden context.
A useful evaluation fixture contains: trusted system policy, an untrusted document, available tool definitions, expected allowed calls, forbidden calls, and an event recorder. The pass condition is behavioral. Do not only check the assistant's final prose. An agent that says it refused after already calling a destructive tool has failed.
Keep the model and client configuration pinned, set temperature or sampling controls when available, run repeated trials for nondeterministic paths, and record tool events. Score unauthorized tool attempts, sensitive argument disclosure, cross-tenant access, and whether confirmation was bypassed. Do not place real secrets in canaries. Use recognizable synthetic markers.
The detailed MCP prompt injection attack testing tutorial includes payload organization and evaluation design.
Verification: A benign document should support the intended task. A malicious document containing an instruction to call delete_record must cause no delete event and no authorization material to enter model-visible arguments. Confirm through the event recorder and backing store, not through natural-language refusal alone.
Step 7: Detect Secret Leakage and Unsafe Errors
Scan every model-visible and operator-visible channel: tool results, resource text, structured content, protocol errors, stderr, application logs, traces, metrics labels, exception reports, and audit events. Look for API keys, bearer tokens, private keys, connection strings, session cookies, cloud credentials, internal paths, stack traces, and customer data. Pattern matching finds obvious values, while seeded canaries prove whether a known sensitive value crosses a boundary.
Add a recursive scanner to the test file:
const secretPatterns = [
/-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----/,
/(?:authorization|token|api[_-]?key)\s*[:=]\s*[\"']?[^\s\"']{12,}/i,
/postgres(?:ql)?:\/\/[^\s]+/i
];
function findLeaks(value: unknown): string[] {
const serialized = JSON.stringify(value);
return secretPatterns
.filter((pattern) => pattern.test(serialized))
.map((pattern) => pattern.source);
}
it('does not leak secrets in errors', async () => {
const result = await client.callTool({
name: 'delete_record',
arguments: { id: 'rec-1', authorization: 'invalid-secret-shaped-value' }
});
expect(findLeaks(result)).toEqual([]);
});
Regexes create false positives and miss unfamiliar formats, so combine them with entropy-aware scanners where appropriate and exact synthetic canaries injected into test dependencies. Redact before serialization, not only in a log viewer. Return stable public error codes and keep restricted diagnostic detail in a protected sink. Never echo an invalid credential back to the caller.
Follow the complete MCP server secret leakage test guide to cover logs, traces, resources, and failure paths systematically.
Verification: Insert a fake canary such as QA_CANARY_DO_NOT_EXPOSE_7F3A into a mocked upstream error. The security test must fail if that marker appears in any captured channel. Then enable server-side redaction and confirm the test passes while an authorized operator can still correlate the error by a nonsecret request ID.
Step 8: Test Transport, Sessions, and Lifecycle Security
Stdio reduces network exposure but still inherits local process risks. Verify executable paths, environment allowlists, working directories, child-process permissions, and stderr handling. Do not pass broad parent environments containing unrelated credentials. Ensure the client launches the expected binary and cannot be tricked by path replacement.
For remote HTTP transports, test authentication on every request, TLS termination, allowed origins when browser clients are possible, request size and time limits, rate controls, and session ownership. A session identifier is not proof of identity. Reject a valid session used by a different principal, tenant, or client context. Test disconnect, reconnect, cancellation, duplicate delivery, expiration, logout, and concurrent calls.
Lifecycle tests uncover subtle failures. Cancel a long tool call and verify downstream work stops or reaches a documented safe state. Retry the same request and confirm idempotency for operations that promise it. Send concurrent updates to the same object and verify authorization is evaluated at execution time, not only when the request is queued. Restart the server and prove that stale approvals or sessions are not silently trusted.
Dependency security also matters. Lock dependencies, review transitive updates, generate a software bill of materials if your organization requires it, and scan the server container or host. Treat a third-party MCP server as privileged integration code. Review its source, publisher, requested credentials, tool inventory, network destinations, and update channel before connection.
Verification: Attempt an unauthenticated request, a cross-user session reuse, an expired session, a request above the size limit, and a cancellation. Each must fail safely without sensitive response data or an unintended side effect. Confirm the server remains healthy and emits a correlated audit event.
Step 9: Build a CI Security Gate and Evidence Trail
Organize the suite into fast deterministic checks and isolated adversarial evaluations. Run schema, inventory, permission, and leakage tests on each pull request. Run broader model-driven injection scenarios on a schedule or before release, while keeping a small stable set in the pull request gate. Never let a live model evaluation reach production tools or real customer data.
A practical gate evaluates more than pass count:
- No unapproved capability additions or schema weakening.
- No denied request produces a side effect.
- No synthetic secret appears in captured outputs.
- No malformed request crashes or hangs the server.
- No prompt injection scenario triggers a forbidden event.
- Every sensitive action produces an audit event without sensitive arguments.
Store the server commit, SDK lockfile, client configuration, model identifier when used, fixture version, random seed, subject identity, tool event timeline, sanitized results, and policy version. This evidence makes a failure reproducible and helps reviewers distinguish a product regression from model variability. Retain it according to your privacy policy because prompts and tool arguments can contain sensitive data.
Security tests should block release for clear boundary violations. Flaky model evaluations need thresholds and investigation rules, but deterministic authorization or leakage failures should not be retried into a pass. Assign owners and response times. A security suite without triage ownership becomes a dashboard instead of a control.
Verification: Introduce three controlled mutations in a branch: expose debug_dump, skip the delete authorization check, and return a canary in an error. Confirm that separate named tests fail for all three. Remove the mutations and save the clean run plus policy version as release evidence.
The Complete Series
Use these focused tutorials to turn each major control into a deeper, reusable suite:
- Test MCP prompt injection attacks: Build direct and indirect injection scenarios and verify actual tool behavior.
- Validate MCP tool permission boundaries: Create identity, scope, tenant, consent, and side-effect matrices.
- Fuzz MCP tool arguments with JSON Schema: Generate boundary and malformed inputs while preserving regression seeds.
- Test MCP server secret leakage: Scan responses, errors, resources, logs, and traces with patterns and canaries.
Together, the pillar and four labs cover the most important MCP-specific security test surfaces. Pair them with implementation-level injection tests, infrastructure scanning, dependency review, and your organization's incident response process.
Troubleshooting
The client times out while starting the stdio server -> Make sure the server writes only MCP messages to stdout. Move debug output to stderr, verify the tsx loader command, and run the server once outside Vitest to expose startup errors.
A malformed argument throws instead of returning isError -> Distinguish protocol errors from tool execution errors in the assertion. Capture the client exception, assert its stable public code, and still verify that no side effect occurred. Do not weaken the test just to accept an uncontrolled stack trace.
Unknown object properties are accepted -> Check how the SDK converts the Zod schema and whether objects strip extra keys. Set an explicit strict-object policy for sensitive tools, or document and test stripping so callers cannot mistake ignored security fields for enforced controls.
Permission tests pass but data still changes -> Your test is checking only the returned content. Query the backing store, mock the downstream API, or inspect the queue and audit stream. Security outcomes require state-based assertions.
Secret scanning reports many false positives -> Separate high-confidence credential signatures, exact canaries, and heuristic findings. Block on exact canaries and validated patterns, then review heuristic matches without deleting broad coverage.
Prompt injection results vary between runs -> Pin the model and agent configuration, record complete tool events, repeat scenarios, and use behavioral thresholds. Keep authorization enforcement deterministic on the server so model variability cannot become the only defense.
Where To Go Next
Start with the control most likely to produce a severe outcome in your system. If tools change data, build the permission matrix first. If the server retrieves third-party content, prioritize prompt injection. If schemas are large or generated, automate fuzzing. If the integration touches credentials or customer data, add canary-based leakage tests immediately.
Then add the four series tutorials to CI, connect findings to owners, and run a short threat-model review whenever tools, resources, identities, transports, or downstream systems change. For career preparation, use the questions below to explain not just attacks, but also how you would prove a defense works.
Interview Questions and Answers
Q: What is the most important principle in MCP security testing?
Treat every model request and every external content source as untrusted. Enforce authorization, validation, and tenant isolation in deterministic code at the server boundary. Then verify the final state, because a refusal message does not prove that a side effect was prevented.
Q: How do protocol tests differ from model-driven MCP tests?
Protocol tests call list, read, or tool operations directly and validate deterministic server behavior. Model-driven tests place a model and agent loop in front of the server to measure whether malicious content influences tool selection or arguments. You need both because protocol correctness cannot prove agent behavior, and agent refusal cannot replace server controls.
Q: How would you test an MCP tool's authorization?
Build a matrix of identities, roles, scopes, tenants, resource ownership, consent state, and session state. Assert explicit denial for every forbidden combination and verify the backing system did not change. Include horizontal, vertical, stale-session, and replay cases.
Q: What should an MCP argument fuzzer generate?
Generate missing required fields, wrong types, boundary lengths and numbers, invalid patterns and formats, Unicode edge cases, unexpected properties, deep nesting, and oversized payloads. Add domain-invalid values that pass syntax but violate ownership or workflow rules. Save every failure as a deterministic regression seed.
Q: How do you prove a prompt injection defense works?
Run hostile content through an isolated real agent and record all tool events and final state. The pass condition is no forbidden tool attempt, no sensitive argument disclosure, and no unauthorized side effect. Repeat nondeterministic scenarios and keep server authorization as the final deterministic control.
Q: How do you find secret leakage in an MCP server?
Capture results, resources, errors, stderr, logs, traces, metrics labels, and audit events. Scan with high-confidence patterns and exact synthetic canaries, then trigger both success and failure paths. Verify redaction occurs before data reaches model-visible or broadly accessible sinks.
Q: What evidence belongs in an MCP security test report?
Record code and policy versions, dependency lockfile, test identity, fixture version, model and client configuration, random seed, request timeline, tool events, sanitized response, backing-state assertion, and audit correlation ID. This evidence makes the result reproducible without retaining real secrets.
Best Practices
- Deny by default and authorize inside every sensitive tool using trusted identity.
- Keep tool authority narrow, descriptions accurate, results minimal, and credentials outside model context.
- Validate at schema, business-rule, authorization, and downstream integration layers.
- Assert state after denied and cancelled operations.
- Separate untrusted content from trusted instructions and label provenance when the client supports it.
- Use synthetic data, isolated accounts, restricted networks, and reversible side effects.
- Capture structured tool events and sanitized audit records.
- Fail builds on deterministic permission, capability, crash, and leakage regressions.
- Revisit the threat model when a tool, resource, prompt, identity, or transport changes.
Avoid common mistakes such as relying on the model to enforce access control, hiding a tool only in the UI, treating JSON Schema as business authorization, scanning only successful responses, trusting a natural-language refusal, using production secrets as canaries, or approving capability snapshots without reviewing the security impact.
MCP Security Testing Complete Guide 2026 Conclusion
A strong MCP security program tests the complete chain from untrusted content to model behavior, protocol messages, server policy, tool implementation, downstream state, and operational telemetry. The repeatable core is simple: inventory capabilities, deny unauthorized calls, fuzz every schema, scan every output, and prove the final state.
Run the sample harness, replace its toy policy with your real identity and tenant rules, and deliberately mutate each control to prove the suite detects failure. Then use the complete series to deepen prompt injection, permission, fuzzing, and secret leakage coverage before your MCP server handles sensitive data or consequential actions.
Interview Questions and Answers
What is the most important principle in MCP security testing?
Treat model requests and external content as untrusted. Enforce authorization, validation, and tenant isolation in deterministic server code. Verify the backing state because a refusal message alone does not prove that no side effect occurred.
How do protocol tests differ from model-driven MCP tests?
Protocol tests call MCP operations directly and verify deterministic server behavior. Model-driven tests evaluate whether hostile context changes an agent's tool choices or arguments. Both are required because protocol correctness does not prove agent behavior, while model behavior cannot replace server enforcement.
How would you test MCP tool authorization?
Build a matrix across identities, roles, scopes, tenants, ownership, consent, and session states. Assert explicit denial for forbidden combinations and inspect the backing store for absence of side effects. Include horizontal escalation, vertical escalation, replay, revocation, and expiration cases.
What should an MCP argument fuzzer generate?
Generate omitted required fields, wrong types, boundary values, invalid patterns, Unicode edge cases, extra properties, deep nesting, and oversized payloads. Also generate values that satisfy syntax but violate ownership or workflow rules. Save any failure as a deterministic regression seed.
How do you prove a prompt injection defense works?
Run malicious content through an isolated agent and record every tool event plus the final backing state. Require zero forbidden calls, sensitive argument disclosures, and unauthorized side effects. Repeat nondeterministic scenarios, but keep server-side authorization as the deterministic final control.
How do you test an MCP server for secret leakage?
Capture results, resources, errors, stderr, logs, traces, metrics, and audit events across success and failure paths. Scan them with credential patterns and exact synthetic canaries. Verify redaction occurs before serialization into model-visible or broadly available channels.
What evidence should an MCP security test retain?
Retain the server and policy versions, dependency lockfile, test identity, fixture version, client and model configuration, seed, request timeline, tool events, sanitized response, state assertion, and audit correlation ID. This makes a result reproducible without storing real credentials.
Frequently Asked Questions
What is MCP security testing?
MCP security testing verifies that Model Context Protocol clients and servers expose only approved capabilities, enforce permissions, reject hostile inputs, resist prompt injection, and prevent sensitive data leakage. It combines deterministic protocol and integration tests with isolated model-driven adversarial evaluations.
How do I start testing an MCP server for security?
Start by inventorying tools, resources, and prompts, then map each sensitive capability to an identity and authorization policy. Automate negative permission tests, malformed argument tests, side-effect assertions, and secret scans before adding model-driven prompt injection scenarios.
Can JSON Schema secure an MCP tool?
JSON Schema can reject malformed types, missing fields, invalid formats, and boundary violations. It cannot decide whether a user owns a resource or may perform an action, so server-side business validation and authorization are still required.
How should prompt injection be tested in MCP?
Place direct and indirect malicious instructions in every untrusted content path, then run an isolated agent and record its tool events. Pass only when no forbidden call, sensitive disclosure, or unauthorized side effect occurs, regardless of the final natural-language response.
Should MCP security tests run against production?
Destructive, fuzzing, and model-driven adversarial tests should run in an isolated environment with synthetic data, restricted credentials, and reversible side effects. Production can use safe configuration checks and monitoring, but it should not be the first place hostile payloads are exercised.
How can an MCP server leak secrets?
Secrets can appear in tool results, resources, structured errors, stack traces, stderr, logs, traces, metrics labels, or audit events. Test success and failure paths with synthetic canaries and redact data before it reaches a model-visible or broadly accessible sink.
What MCP security tests belong in CI?
Run capability inventory, schema validation, permission denial, side-effect, crash resistance, and secret canary tests on pull requests. Keep a stable subset of model-driven injection tests in the gate and run broader nondeterministic evaluations on a schedule or before release.
Related Guides
- AI Agent Testing Complete Guide (2026)
- API security testing basics: A Practical Guide (2026)
- Cloud-Native Performance Testing Complete Guide (2026)
- Event-Driven API Testing Complete Guide (2026)
- Modern GraphQL API Testing Complete Guide (2026)
- Penetration testing basics: A Complete Guide for QA (2026)