Server and Python SDK 3.2.1
DeepIntShield Server 3.2.1 and Python SDK 3.2.1 - 16 October 2026.
This is a correction release on 3.2.0. It aligns the SDK, gateway and documentation reference at 3.2.1; every 3.2.0 client keeps working unchanged. Check the version and enabled capabilities of your own gateway: a documentation release does not establish what is deployed.
MCP tool schemas only for callers that can use them
Section titled “MCP tool schemas only for callers that can use them”A virtual key can be bound to MCP servers while the workspace has Agentic
Security on. Until now the gateway attached those servers’ tool schemas to
every chat and Responses request made with such a key. Tool execution,
however, needs an enabled agent associated with the key, so a plain
application call - no agent, a model that decided to use one of the offered
tools - ended with 503 mcp_tool_authorization_unavailable at the model’s
first tool call, and frameworks that read the content of that turn saw an
empty answer.
The governed catalog now asks the authorizer first whether the caller could be
authorized at all. A key with no enabled agent association (or an invalid
X-Agent-Subject, or an active organization that does not own the workspace)
is shown no MCP schema; the request completes with the tools it brought. Keys
with an enabled agent see and execute their tools exactly as before, and the
per-call authorization boundary is unchanged: nothing is granted, something is
no longer offered. With Agentic Security off, the key’s own MCP allow-list
remains the boundary and the catalog is unchanged. The answer is reused for
five seconds per caller, so a key that carries MCP bindings pays no extra
lookup on its ordinary requests.
Operators who want a plain application key to execute MCP tools under Agentic Security associate it with an approved agent, as before; the difference is that until they do, the key’s requests succeed without tools instead of failing.
API paths that do not exist
Section titled “API paths that do not exist”An unknown /api/…, /v1/… or /scim/… path now answers the standard JSON
404 envelope (Route not found: <path>) instead of a 200 carrying the
dashboard’s HTML. Scripts that probed a route a build does not have - or
mistyped one - get a status they can act on. Dashboard pages are unaffected.
The cost-optimization cascade and the model policy
Section titled “The cost-optimization cascade and the model policy”With Cost Optimization’s cascade routing on, the gateway may answer a request
with a cheaper tier’s model. The substitution happens before the guardrail
stage, which judges the model it finds on the request. When the caller’s
guardrail policies include a model-inventory check (the OWASP LLM03 supply-chain
card, a “whitelist” of models), a tier outside that list was dispatched and
then refused: a gpt-4o-mini request - a model the policy allows - ended with
403 guardrail_blocked: Model gemini-2.5-flash violated the configured whitelist policy, naming a model the caller never asked for.
The cascade now asks the guardrails plugin first. The question runs on the plugin’s in-process fast path with the caller’s own policy selection and no content, so only model-dependent checks can answer it; it adds no round-trip, no external adapter call and no evidence record. A refused tier is simply not used - the request keeps the model its caller asked for and the ordinary evaluation decides as before. Permitted tiers, keys without guardrails and deployments without the embedded runtime behave exactly as they did.
Two things the release does not change, because they are the operator’s
configuration: a tier the policy permits is still dispatched in place of the
requested model (the response’s model names what answered), and a requested
model outside the policy’s list is still refused when the cascade leaves the
request untouched. A workspace whose tests or applications must reach a
specific model keeps the cascade’s virtual-key scope away from those keys.
Gemini requests with a forced function call
Section titled “Gemini requests with a forced function call”A request in Gemini’s own format (/v1beta/models/{model}:generateContent
through the gateway’s Gemini integration) can carry
toolConfig.functionCallingConfig with mode ANY and one
allowedFunctionNames entry: the model must call that function. The gateway
converted this to a tool_choice of type function without the function’s
name, and a provider that validates the choice - any OpenAI-compatible
provider, including the fallback a workspace may route Gemini models to -
answered 400 Missing required parameter: 'tool_choice.name'.
The conversion is now complete in both directions: one forced function
becomes a tool_choice that names it; several allowed names become an
allowed_tools choice (required for ANY and VALIDATED, auto
otherwise); NONE becomes "none"; ANY without names stays "required".
The reverse conversion, used when a Responses-format request reaches a Gemini
model, produces the same toolConfig again. Requests that name no function
are unchanged.
Added 18 October: a function declared with the google-genai SDK’s
parameters_json_schema (a plain JSON Schema in place of the OpenAPI-style
parameters) reached the provider with no parameters at all, so the model
called it with empty arguments. The JSON Schema object now becomes the
function’s parameters when no OpenAPI-style schema is given; a non-object
value is ignored as before.
Deployment and operations
Section titled “Deployment and operations”- Redis 8. Dropping a semantic-cache index that does not exist is accepted
on Redis 8’s query engine (
SEARCH_INDEX_NOT_FOUND Index not found) as it is on Redis Stack 7 (Unknown Index name). Namespace deletion no longer fails on a Redis 8 store whose index was never created. - Plugin modules build standalone.
plugins/jsonparser,plugins/mockerandplugins/mcpcacheresolve their siblingcoreandframeworkmodules from the checkout, and thego.mod/go.sumoflogging,response_consistency,semanticcache,governanceandmcpcacheare complete, sogo test ./...runs in every plugin directory without network access to a private module path. - Handlers log through a default logger. The HTTP handlers package starts
with a no-op logger and ignores
SetLogger(nil), so no request path can dereference a nil logger before the server installs the real one; the request-logging step of the CORS middleware panicked on a nil logger, which showed only when a handler test ran on its own. - Server, UI, MCP handshake (
DeepIntShieldMCPVersion), Helm chart (versionandappVersion) and npm package metadata read 3.2.1. Container and npm publication are separate release steps. - Cloud Run tier: one guard shared secret, webhook outbox on (18 October,
deployment/tiers/cloud-cheap). The gateway’s fine-tune (LoRA) page and the models sidecar’s runtime authentication both needDEEPINTSHIELD_GUARD_SHARED_SECRET, and the tier’s service template mounted it into neither container (“Configure the same DEEPINTSHIELD_GUARD_SHARED_SECRET on the gateway and models sidecar”).deploy_cloudrun.shnow createsdeepintshield-cheap-guard-shared-secretonce in Secret Manager (seeded fromDEEPINTSHIELD_GUARD_SHARED_SECRETwhen given, otherwise generated; never rotated on its own) and the template mounts its latest version into the server and models containers. The same template renders the durable webhook outbox on (DEEPINTSHIELD_WEBHOOKS_ENABLED, defaulttrue) with its object-store settings: namingDEEPINTSHIELD_WEBHOOK_S3_BUCKETselects the S3-compatible payload store, otherwise the file backend keeps payloads on the instance’s ephemeral disk and the deploy warns. The CD workflow, the GitHub sync scripts,env.example, the smoke test and the contract tests carry the variables; a deploy creates a new revision and so restarts both containers together. The GKE Helm tier already wired the secret and is unchanged.
Python SDK 3.2.1
Section titled “Python SDK 3.2.1”- Aligned with Server 3.2.1; the inference, guardrail, RAG, MCP and Agentic APIs are unchanged from 3.2.0.
- A program whose server run ended because its identity cannot use one runs
locally with the fingerprint the administrator approved: when the gateway’s
capabilities answer carries
report_program_bundle, local discovery reports the program bundle the submission reported, without a second review. shield.mcp.connect(identity=True)names the reason when the gateway refuses to issue workload credentials (agent_not_registered, for example). The refusal happens inside the MCP transport’s request hook, which the transport answered with a bareCancelledError: Cancelled via cancel scope; the session now raises the recorded refusal as its connection error, and as the operation’s error when a credential renewal fails mid-session. A real cancellation without such a refusal still propagates unchanged.- A LangGraph node built from Runnables - a
RunnableParallel, a chain such asitemgetter("messages") | model, aRunnableBranch, aToolNodeor a compiled subgraph - is covered in the blueprint. The SDK already gated such nodes at run time but described them as a local tool with no source, so the gateway refused every graph of that shape asblueprint_coverage_incompleteand quarantined the reporting agent. The node’s source is now the composition it dispatches (class names and keys only, never arguments or configuration) with the source of every Python callable and application class inside it, which is what the gateway scans, in the blueprint manifest and in the discovery report alike; the node becomes a governed tool keyed by its node name. A callable whose source cannot be read still leaves the node uncovered, as a plain function without source does. - A program with no application code of its own (an agent made of
instructions, a model and an output type, with no tools, hooks or
callbacks) reports its definition as the code to scan. The gateway refuses
a discovery report with no code artifact at all as
blueprint_coverage_incompleteand quarantines the reporter, so such programs could never be enrolled; the definition artifact (framework, agent, model, bounded instructions, output type, handoffs) is scanned like any source. A program whose only tools are remote MCP tools is unchanged. - An
autogen_agentchat(or AG2) agent run without a team is discovered as its own one-participant AutoGen program, with its model and, when it has no tools, its definition (the system message) as the code to scan. The generic path had captured the framework’srunmethod or nothing at all, so the report read asblueprint_coverage_incompleteand quarantined the reporting agent.
QA portal and test catalogue
Section titled “QA portal and test catalogue”The internal test catalogue that exercises every gateway route was corrected
alongside this release: management-token requests name the QA workspace
(QA_ORG_ID, QA_WORKSPACE_ID shared settings, sent as X-Active-Tenant-Id
and X-Active-Workspace-Id); a route behind a plan gate reports Blocked
with the locked feature instead of Fail when the workspace’s plan lacks it;
unknown-ID probes use an ID of the shape the handler validates; and a source
test that can skip itself is no longer a case that expects it to pass. The
forced-tool-call fidelity cases now tell the model that the JSON object is the
tool’s complete arguments object (the earlier “return this exact JSON object”
made gpt-4o-mini place the whole object inside the single string argument),
and the large-identifier payload is a 20-digit string: a 13-19 digit run is
payment-card data to the guard’s PII detector by design (demo numbers without
a checksum must still be caught), so the earlier 16-digit value was refused by
any workspace with a card policy before it reached a model. A Chat
Completions case that forces one named function expects finish_reason
stop, which is what OpenAI returns for a forced specific function
(tool_calls is the answer to auto and required); the earlier
expectation held only while another provider answered in OpenAI’s place.
And a source-test case whose Go or pytest test skips itself on the tester’s
machine (a fixture to build, a store to start, a key to set) prints
Status: BLOCKED with the skip reason instead of FAIL; a Go test that
fails while one of its subtests skips still reads FAIL, since the skip is
a precondition only when the named test did not fail.
Every chat case of the catalogue now names gpt-4o-mini, including the
cases written against the Anthropic Messages, Gemini generation and Bedrock
Converse client libraries, which the gateway converts to the provider that
serves the model: one OpenAI provider key, one model-inventory entry and one
model configuration carry the whole catalogue, and a provider’s own quota or
a per-model cap no longer stops a run. Only the settings with no
gpt-4o-mini equivalent keep their model (embeddings, speech-to-text, the
realtime session, document OCR, Gemini’s named content caching). Note for
anyone running the catalogue: a QA key inside the cost-optimization
cascade’s scope has its short requests answered by the cheapest tier, and a
model-inventory check that does not list a model refuses its requests. The
setup script checks both and, with --apply-model-policy, takes the QA keys
out of the cascade’s virtual-key scope and adds every model the cases name
to the enabled policies’ model-inventory checks; without the flag it reports
the exact change to make.
Corrected on 18 October, after the deployment of the release: fourteen
sample-application cases (and the 72 that share their check in the other
sample applications) called json.loads on the model’s raw answer and
failed when gpt-4o-mini wrapped the JSON in a Markdown code fence or
prefaced it; they now read the JSON object in the answer, with their value
checks unchanged. The API contract cases of a feature the gateway mounts
only with a server flag (the webhook outbox, DEEPINTSHIELD_WEBHOOKS_ENABLED)
read Blocked with the flag’s name, like a plan gate, when the deployment
does not mount it. And a note for anyone running the agentic cases: one
agent identity holds one blueprint at a time, and with the workspace’s
blueprint policy on runtime enforcement every distinct program needs its
own approval while its scan is the agent’s latest, so the cases run one at
a time (run, approve, run again) unless the policy admits clean deltas.
Upgrade
Section titled “Upgrade”python -m pip install --upgrade "deepintshield==3.2.1"# with gateway runtime authentication and LangGraph:python -m pip install --upgrade "deepintshield[runtime,langgraph]==3.2.1"The console ships inside the server image, so Server 3.2.1 and its UI upgrade
together. No configuration changes, no new variables and no migration are
required by the server itself; the Cloud Run (cloud-cheap) deployment tier’s
deploy script creates the guard shared secret and renders the webhook outbox
settings on its own (see “Deployment and operations”). A workspace whose plain
application keys carry MCP bindings will see
those keys’ requests complete without MCP tools until the keys are associated
with an approved agent; nothing else changes for existing clients. For the
3.2.0 changes (two-run registration, deferred activation, registration
e-mails), see Server and Python SDK 3.2.0.