# ऐसे fake credentials से MCP tools test करें जो सुरक्षित रूप से विफल हों

MCP tools को सुरक्षित ढंग से test करने के लिए production token को `TEST_TOKEN` नाम की string से बदल देना पर्याप्त नहीं है। Tool fake value को parse कर सकता है, सफलता का सरल संदेश लौटा सकता है, फिर भी पहली बार किसी agent को वास्तविक permission boundary, approval denial, धीमे provider या अधूरे write का सामना होने पर विफल हो सकता है।

Test environment को एक साथ दो बातें साबित करने देनी चाहिए: tool अपेक्षित request भेजता है और test गलत होने पर production में वास्तविक नुकसान नहीं कर सकता। Disposable accounts और fake credentials इस काम के अलग हिस्से संभालते हैं। इन्हें अलग controls की तरह रखें।

## Fake credentials parsing test करते हैं, authority नहीं

Fake credential केवल यह साबित करती है कि tool सही आकार वाली credential value संभालता है। इससे यह साबित नहीं होता कि provider उसे स्वीकार करता है, उसके पास अपेक्षित scopes हैं या revoked value साफ तरीके से विफल होती है।

यह अंतर इसलिए धुंधला पड़ता है क्योंकि कई teams हर nonproduction secret को fake secret कहती हैं। वास्तव में तीन अलग चीजें होती हैं:

- Synthetic string केवल local stub में मौजूद होती है। वह कहीं authenticate नहीं कर सकती।
- Test credential real provider के विरुद्ध authenticate होती है, लेकिन केवल disposable tenant या project के भीतर।
- Restricted production credential production के विरुद्ध authenticate कर सकती है, भले उसका scope छोटा दिखे।

पहला विकल्प unit और contract tests के लिए है। दूसरा integration tests के लिए। तीसरे को automated agent test suite में नहीं होना चाहिए। Production customer record पढ़ सकने वाली credential पहले ही उस boundary को पार कर चुकी है जिसे आप सुरक्षित रखना चाहते थे।

Fake values का format वही रखें जो आपका code वास्तव में प्राप्त करता है। यदि API client prefix या minimum length के बिना token अस्वीकार करता है, तो ऐसा synthetic value रखें जो local validation पार कर सके। Real token copy करके एक character न बदलें। लोग fixtures को issue trackers, terminal transcripts और chat threads में paste कर देते हैं। लगभग असली secret आपको testing के लाभ के बिना handling का पूरा risk देता है।

अच्छे fixture के नाम में यह स्पष्ट होता है कि उसके पास कौन-सी authority नहीं है। उदाहरण के लिए `stub_token_no_network`, `token123` से अधिक जानकारी देता है। `billing_test_writer` जैसा test credential reference बताता है कि वास्तविक value agent नहीं, secret store resolve करता है। Reference को स्थिर रखें और आवश्यकता होने पर underlying test credential rotate करें।

Successful mock को authorization न समझें। Stub के `200` लौटाने का अर्थ केवल इतना है कि उसने आपके बनाए contract को स्वीकार किया। यह आपके request construction के बारे में उपयोगी प्रमाण है, provider के scope model के बारे में नहीं।

## Invalid input, rejection और operational failure अलग रखें

Bad argument, denied action और broken dependency के लिए agent को अलग guidance चाहिए। यदि MCP tool तीनों को `request failed` बना देता है, तो agent उन actions को retry करेगा जिन्हें रोक देना चाहिए और उन actions को छोड़ देगा जिन्हें सुधारा जा सकता था।

छोटी outcome vocabulary रखें और उसे tool result में बनाए रखें। मैं पाँच classes उपयोग करता हूँ:

- Validation error: agent ने invalid या अधूरा argument दिया। Agent call सुधार सकता है।
- Authentication failure: credential अनुपस्थित, expired, malformed या revoked है। वही call दोहराने से मदद नहीं मिलेगी।
- Authorization rejection: identity authenticate हो गई, लेकिन permission नहीं है, या human ने approval deny कर दिया। Agent को boundary के आसपास रास्ता नहीं खोजना चाहिए।
- Conflict: request valid थी, लेकिन वर्तमान resource state पर लागू नहीं हो सकती। Agent को पहले current state fetch करनी पड़ सकती है।
- Operational failure: timeout, connection failure, rate limit या provider fault। Action को सुरक्षित रूप से retry किया जा सकता हो तो retry उचित हो सकती है।

HTTP इस विभाजन के परिचित हिस्से देता है। RFC 9110 में `401 Unauthorized` को authentication challenge बताया गया है, हालांकि इसका ऐतिहासिक नाम भ्रमित करने वाला है, और `403 Forbidden` request पूरा करने से इनकार है। Provider इन codes का सही उपयोग न भी करे, इसलिए body और documented error codes भी test करें। Agent behavior केवल status code से न बनाएँ।

Model Context Protocol tool result content के साथ `isError` flag भी स्वीकार करता है। Tool call विफल होने पर इसका उपयोग करें, लेकिन actionable category को text या उस structured content में रखें जिसकी आपका client अपेक्षा करता है। Transport-level JSON RPC error और tool-level error अलग हैं। JSON RPC errors को malformed protocol requests या unavailable methods तक सीमित रखें। जब tool चला हो और upstream action विफल या reject हुआ हो, तो सामान्य `tools/call` result में `isError: true` लौटाएँ।

यह response shape agent को ठोस जानकारी देता है:

```json
{
  "jsonrpc": "2.0",
  "id": 42,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "authorization_rejected: identity billing_test_writer cannot create invoices in tenant test-acme. Request a role change or stop."
      }
    ],
    "isError": true
  }
}
```

इस result में bearer token, authorization header, पूरी upstream request या raw provider error न रखें। Error messages agent के context window का हिस्सा होती हैं और उन systems में रखी जा सकती हैं जिन्हें आप नियंत्रित नहीं करते। Error में behavior guide करने जितनी जानकारी हो, forensic dump नहीं।

## Disposable accounts के लिए कड़ी boundary चाहिए

Disposable account तभी सुरक्षित है जब उसके पास production resources तक पहुँचने का कोई रास्ता न हो। अलग email address और `test` label यह boundary नहीं बनाते।

Provider की सबसे मजबूत isolation unit से शुरुआत करें। यह अलग organization, tenant, cloud project, database या self hosted instance हो सकती है। Test account को उसी unit में रखें और verify करें कि identifier, shared role या default configuration value के माध्यम से account production unit में switch नहीं कर सकता।

फिर account को intended test के लिए आवश्यक और केवल उतनी ही permissions दें। यदि tool tests में invoices बनाता है, तो test identity को test invoices बनाने और list करने की अनुमति चाहिए। उसे export permissions, administrator access या shared payment configuration की पहुँच नहीं चाहिए। Broad test access setup को छोटा करता है, लेकिन यह छिपा देता है कि tool को वास्तव में कौन-सी permissions चाहिए।

Suite द्वारा बनाए हर resource में explicit test run marker लगाएँ। Supported metadata field, description, tag या name का उपयोग करें। Marker cleanup को सुरक्षित बनाता है और leaked test artifacts पहचानने योग्य करता है। Test tenant में मौजूद हर चीज delete न करें। कोई दूसरा developer वहाँ bug reproduce कर रहा हो सकता है।

Minimal configuration इस तरह दिख सकती है:

```yaml
run_id: mcp-it-20250308-7f3c
account: agent-tool-test@invalid.example
allowed_tenant: test-acme
resource_prefix: mcp-it-20250308-7f3c-
cleanup_after_minutes: 90
```

यह config एक सामान्य लेकिन महँगी गलती रोकती है: developer shell का environment variable checked configuration पर भारी पड़ता है और test runner गलत tenant की ओर चला जाता है। कुछ भी create करने से पहले setup code को current tenant identity fetch करके `allowed_tenant` से मिलानी चाहिए। अंतर हो तो पहले write से पहले रुक जाएँ।

Disposable का अर्थ anonymous या ownerless नहीं है। Owner तय करें, credential कैसे जारी हुई यह दर्ज करें और expiry जानबूझकर निर्धारित करें। भूली हुई test identity भी access वाली identity है।

## Observable requests के आसपास contracts बनाएँ

अच्छा contract test tool द्वारा भेजी गई request, लौटाए गए result और छिपाई गई information, तीनों जाँचता है। केवल यह assert करना पर्याप्त नहीं कि कोई function call हुआ।

Client के सामने local HTTP stub रखें और उससे method, path, headers, query parameters और body inspect कराएँ। Stub unexpected fields को reject करे। Permissive mocks tool को accidental arguments भेजने की आदत डालते हैं, जब तक real provider उन्हें reject नहीं करता।

`create_invoice` tool के लिए focused test यह call कर सकता है:

```json
{
  "jsonrpc": "2.0",
  "id": 42,
  "method": "tools/call",
  "params": {
    "name": "create_invoice",
    "arguments": {
      "tenant": "test-acme",
      "customer_id": "cus_mcp_it_7f3c",
      "amount_cents": 500,
      "currency": "USD"
    }
  }
}
```

Stub को `POST /v1/invoices` की अपेक्षा करनी चाहिए, confirm करना चाहिए कि authorization header में synthetic test value है और request में run marker शामिल है। वह `inv_mcp_it_001` जैसा fixed identifier लौटा सकता है। MCP result invoice identifier और status दिखाए, authorization header कभी echo न करे।

हर sensitive request field के लिए कम-से-कम एक negative contract assertion लिखें। यदि schema caller द्वारा `authorization`, `base_url`, `account_id` या `tenant` field स्वीकार कर सकता है, तो उन्हें भेजकर देखें। Confirm करें कि tool उन values को reject या ignore करता है जो action को दूसरे account की ओर मोड़ सकती हैं। कई credential leaks custom endpoint के लिए बनाए गए harmless दिखने वाले escape hatch से शुरू होते हैं।

Request serialization को उन bytes पर test करें जो मायने रखते हैं। Provider omitted fields और `null`, empty strings और missing values, तथा numeric values और numeric strings में अंतर कर सकता है। Agents अनपेक्षित argument shapes बनाते हैं, खासकर जब tool description units स्पष्ट नहीं करती। Provider minor units चाहता हो तो schema में `amount` नहीं, `amount_cents` लिखें।

Contract tests में log hygiene भी enforce करें। Structured log event capture करके assert करें कि उसमें credential reference या redacted marker है, synthetic secret स्वयं नहीं। Fake secret तब वास्तविक disclosure problem बन जाती है जब engineers उसे हर जगह print करने लगते हैं।

## Permissions को happy path नहीं, matrix की तरह test करें

एक authorized test identity यह नहीं बता सकती कि tool permissions सही ढंग से संभालता है या नहीं। आपको अलग rights वाली कई identities और expected outcome बताने वाले cases चाहिए।

Matrix को इतना छोटा रखें कि उसे maintain किया जा सके। Write tool के लिए आम तौर पर ये cases उपयोगी हैं:

| Identity state | Requested action | Expected outcome |
| --- | --- | --- |
| test tenant का writer | marked record create करना | success |
| test tenant का reader | marked record create करना | authorization rejection |
| दूसरे test tenant का writer | target tenant में record create करना | authorization rejection |
| revoked credential | marked records list करना | authentication failure |
| expired credential | marked record create करना | authentication failure |

यह matrix एक आम defect पकड़ता है: tool केवल यह जाँचता है कि token मौजूद है, यह नहीं कि token वास्तव में किस tenant का है। Happy path इसलिए सफल होता है क्योंकि writer के पास broad access है। Failure तब दिखता है जब agent को task में tenant identifier मिलता है और client उसे selected credential से bind किए बिना स्वीकार कर लेता है।

जब संभव हो, हर matrix case को real disposable provider के विरुद्ध चलाएँ। Inherited roles, default scopes और delayed revocation के बारे में provider का व्यवहार documentation से अलग हो सकता है। Cases को isolated रखें। यदि एक test role upgrade करता है और दूसरा test उसी role के absent होने की अपेक्षा करता है, तो parallel runs authorization bugs जैसी failures बनाएँगे।

केवल stub में forbidden responses बनाकर suite को पूरा न मानें। Stub यह confirm करता है कि आपका error mapper `403` पहचानता है। Real test identity यह confirm करती है कि credential issuance process, provider setup और tool routing वास्तविक rejection देते हैं।

एक उपयोगी अपवाद है: उन provider responses को test करना जिन्हें भरोसेमंद ढंग से trigger नहीं किया जा सकता, जैसे malformed JSON या invalid content type। इन cases का स्वामित्व stub के पास है। उद्देश्य purity नहीं, यह जानना है कि प्रत्येक test कौन-सा प्रमाण देता है।

## Test code से पहले writes की cleanup योजना बनाएँ

State बदलने वाले हर tool के लिए success test लिखने से पहले तय करें कि test उस state को कैसे हटाएगा या निष्क्रिय करेगा। Cleanup का वर्णन न कर सकें तो अलग target या अलग operation चुनें।

ऐसे operations को प्राथमिकता दें जो short lived test project में records बनाते हैं। Email भेजने, card charge करने, shared secret rotate करने, deployment trigger करने या किसी real third party से संपर्क करने वाले test calls से बचें। Provider का sandbox भी उस endpoint पर webhooks भेज सकता है जिसे आपने वर्षों पहले configure किया था। केवल API label नहीं, आसपास के effects भी जाँचें।

Unique run marker रखें और `finally` block या उसके equivalent में cleanup करें। Cleanup को ऐसा resource संभालना चाहिए जो कभी बना ही नहीं, retry के बाद दो बार बना हो या partially configured account में मौजूद हो। Idempotent cleanup provisioning के बीच में test विफल होने पर समय बचाता है।

एक महत्वपूर्ण failure ऐसा है: Tool create request भेजता है। Provider record बना देता है, लेकिन response लौटाने से पहले connection टूट जाती है। Agent operational failure देखता है और retry करता है। Idempotency value के बिना retry duplicate बनाती है। Run marker के बिना cleanup दोनों records को सुरक्षित रूप से नहीं खोज सकता।

Create request को run identifier और logical action से बनी idempotency value दें, transport attempt से नहीं। पहली attempt और retry में वही value होनी चाहिए। फिर stub से dropped response case test करें: वह पहली request record करे, connection बंद करे और वही idempotency value दोबारा मिलने पर success लौटाए। Assert करें कि provider side पर एक ही record है।

Provider idempotency support न करता हो तो write retry करने से पहले unique external reference से lookup कराएँ। Concurrency में यह approach race कर सकती है, इसलिए बचा हुआ risk document करें। Generic HTTP client `POST` को retryable मानता है, केवल इस कारण money movement या irreversible writes को चुपचाप retry न करें।

## Timeouts और rate limits agent का खराब behavior दिखाते हैं

Success और `403` सही ढंग से संभालने वाला tool भी dependency धीमी होने पर नुकसान कर सकता है। Agents task पूरा करने की कोशिश में retry करते हैं। Tool को उन्हें सीमित और सच्ची प्रतिक्रिया देनी चाहिए।

Server तक request पहुँचने से पहले connection timeout, server के request पाने के बाद response timeout और provider का `429` response test करें। ये अलग cases हैं। पहले में आम तौर पर side effect नहीं हुआ होता। दूसरे में server write पूरा कर चुका हो सकता है। `429` में retry instruction हो सकती है, लेकिन केवल documented field होने पर और उसका अर्थ सुरक्षित रखने पर उसका उपयोग करें।

Suite को usable रखने के लिए छोटे test timeouts रखें, लेकिन production timeouts को code में test values से replace न करें। Clock या transport configuration inject करें। Hard-coded two-second timeout test सुविधा हो सकती है, लेकिन deploy होने पर outage बन जाएगी।

Assertions में HTTP client के साथ agent-facing behavior भी शामिल करें। Rate limit पर provider का नाम बताने वाली operational failure लौटाएँ और यह स्पष्ट करें कि delay के बाद retry की अनुमति है या नहीं। Write के बाद response timeout पर कहें कि outcome unknown है और caller को idempotency value या external reference से operation lookup करना चाहिए। इसे simple failure कहने से duplicate writes आमंत्रित होती हैं।

Retry को केवल count assert करके test न करें। Record करें कि retry ने idempotency value दोबारा उपयोग की या नहीं, जहाँ आवश्यक था वहाँ wait किया या नहीं और configured limit पर रुक गया या नहीं। अंततः समाप्त होने वाला retry loop भी burst बना सकता है, जो छोटे test tenant को भर दे और वास्तविक defect छिपा दे।

## Approval denial में target untouched रहना चाहिए

यदि human agent actions approve कर सकता है, तो denial path को ऐसे endpoint के विरुद्ध test करें जहाँ side effect आसानी से दिखाई दे। Screen पर denied लिखे होने से यह सिद्ध नहीं होता कि execution layer रुक गई।

Counter, marker record या append-only test event list वाले disposable target की व्यवस्था करें। Agent process शुरू करें, tool call भेजें और session या call deny करें। फिर अलग test observer identity से target को सीधे query करें। Expected count में कोई बदलाव नहीं होना चाहिए।

यह test ordering bug पकड़ता है: tool upstream request शुरू करता है और response की प्रतीक्षा करते हुए approval माँगता है। यदि provider धीमा हो तो demonstration में flow ठीक दिख सकता है, लेकिन approval का उद्देश्य विफल हो जाता है। Authorization decision network connection खोलने या SSH helper शुरू करने से पहले होना चाहिए।

Fresh agent process को उसी process में की गई दूसरी call से अलग test करें। जिन systems में authorization प्रति run दी जाती है, वहाँ ये अलग security promises हैं। ऐसे process को भी test करें जिसकी signing identity अपेक्षित identity से अलग हो। Approval prompt में origin की पर्याप्त जानकारी होनी चाहिए, ताकि approver intended agent को किसी arbitrary local process से अलग पहचान सके।

Local Mac workflow के लिए Sallyport API और SSH credentials को अपने encrypted vault में रखता है और हर नए agent run या selected credential के हर उपयोग पर approval माँग सकता है। इन controls को disposable endpoints के विरुद्ध test करें, upstream permission tests छोड़ने का कारण न बनाएँ।

## SSH tests के लिए discard किया जा सकने वाला target चाहिए

SSH ऐसी failure modes जोड़ता है जिन्हें HTTP examples छिपा देते हैं: host verification, command quoting, environment inheritance, remote shell behavior और dropped connection के बाद बची files। Automated agent SSH tests को developer workstation या shared administration host पर कभी न चलाएँ।

Dedicated account वाली controlled test machine या short lived virtual machine बनाएँ। उस account को बिना उपयोगी data वाली home directory, जहाँ संभव हो restricted command set और दूसरे systems तक पहुँचने वाले credentials से मुक्त environment दें। Dedicated test SSH key इस्तेमाल करें और environment expire होने पर उसे discard करें।

Naive shell construction तोड़ने वाले command arguments चलाएँ। Spaces, quotes, newline characters, dash से शुरू होने वाले paths और shell syntax जैसे दिखने वाले data test करें। Tool को user supplied values को shell string में जोड़ने के बजाय remote command को arguments pass करने चाहिए। यदि remote interface केवल shell text स्वीकार करता है, तो allowed command grammar सीमित करें और उसके बाहर की हर चीज reject करें।

Safe test में target से run marker वाले नाम की file बनवाएँ और फिर उसी exact file को पढ़ें। Rejection test में test account की allowed directory से बाहर का path माँगें और target से denial की अपेक्षा करें। Operational test में command शुरू होने के बाद SSH connection समाप्त करें, फिर देखें कि remote process चलता रहा या नहीं।

Exit status, सीमित standard output और सीमित standard error collect करें। Agent को unlimited command output न लौटाएँ। Large output context खाता है और command output में ऐसे configuration values हो सकते हैं जिन्हें host से बाहर जाना नहीं चाहिए।

## Audit evidence को agent call और side effect जोड़ना चाहिए

Agent test विफल होने पर आपको यह पता होना चाहिए कि tool ने call की या नहीं, provider ने उसे पाया या नहीं और cleanup ने उसे हटाया या नहीं। Console text का ढेर इन प्रश्नों का भरोसेमंद उत्तर नहीं देता।

हर test run को एक identifier दें और उसे MCP request, tool log, provider metadata और cleanup log में लेकर जाएँ। Identifier में credentials न रखें। उपयोगी evidence record में tool name, action category, credential reference, target tenant, request correlation identifier, outcome category और returned resource identifier शामिल होते हैं।

Agent session records को action records से अलग रखें। Session बताता है कि कौन-से process ने run किया और कब revoke किया गया। Action record बताता है कि कौन-सी external call हुई। Correlation identifier से दोनों को जोड़ने पर denial test auditable बनता है: आप दिखा सकते हैं कि agent ने action का प्रयास किया, authorization ने उसे deny किया और कोई matching external action record मौजूद नहीं है।

जब आप नए tool boundary की समीक्षा के लिए test results इस्तेमाल करते हैं, तो tamper evidence महत्वपूर्ण है। Sallyport अपने session और activity journals को encrypted, hash-chained audit log से बनाता है और `sp audit verify` vault key के बिना offline उस chain को verify कर सकता है। यह provider logs का विकल्प नहीं है, लेकिन local tests को altered action history पहचानने का तरीका देता है।

Audit output को secret sink न समझें। Credential references और redacted attributes record करें, फिर tests में इन rules को assert करें। Audit trail action को reconstruct करने में मदद करे, access देने वाली चीज को चुराने की सबसे आसान जगह न बने।

## Release candidate को production access धीरे-धीरे मिलना चाहिए

Tool को लगातार अधिक वास्तविक boundaries से होकर आगे बढ़ना चाहिए: local synthetic stubs, real disposable provider, denied और expired identities, failure injection और workflow में human approval हो तो approval test। Sandbox अलग है, इसलिए सीधे production पर जाना ऐसा है जैसे teams pressure में safety behavior खोज रही हों।

एक छोटा release suite हर change पर चलाएँ और एक deeper suite रखें जो disposable resources कम बार provision करे। Short suite में tool schema, request construction, redaction और representative failures शामिल हों। Deeper suite actual scopes, lifecycle cleanup, revocation और target isolation cover करे।

किसी नए action को production के विरुद्ध अनुमति देने से पहले deliberately denied call और deliberately ambiguous write timeout के evidence को देखें। ये cases बताते हैं कि tool boundary का सम्मान करता है या नहीं और provider के action करने की संभावना होने पर agent को सच बताता है या नहीं। Success paths stage करना आसान है। Refusal और uncertainty paths तय करते हैं कि production access controlled है या reckless।
