Releases: Consiliency/pmcp
Release list
v1.19.3
Security
-
No cross-server credential bleed. The gateway loads every PMCP-stored
credential into its ownos.environ, and the subprocess-spawn paths copied
that whole environment — so each downstream MCP server received every other
server's credentials (e.g.@brightdata/mcpgot the OpenAI key). Downstream
subprocesses now inherit the gateway environment minus PMCP-managed secret
keys (the keys in the userpmcp.env+ project.env.pmcpstores), then get
only their own resolved credential re-applied. This covers both the server
spawn (_connect_stdio) and the paths that execute a server's package code
(start_install/install_server, and theupdate_server/verify_installation
--helpprobes). Non-secret ambient vars (PATH/HOME/NODE_*/proxy/locale)
are preserved. (#96)Scope: only PMCP-managed secrets are stripped — not secrets the operator
exported into the shell or a plain.env. Behavior change: a server that
relied on ambient inheritance of a secret it does not declare (no config
envblock, no manifest credential) no longer receives it. Follow-ups tracked
in #96: sanitizing the trusted-CLI probes as defense-in-depth, and the durable
fix of not loading secrets into the globalos.environat all.
v1.19.2
Fixed
- Per-server credential storage-key namespacing. A server can now store its
credential under a namespaced key (manifestsecret_key, e.g. Bright Data's
BRIGHTDATA_API_TOKEN) while the downstream process still receives the generic
runtimeenv_varit reads (API_TOKEN). This removes the collision risk of
two servers sharing a generic name likeAPI_TOKENin the flat secret store.
Every credential path resolves throughcredential_lookup_keys(namespaced key
first, legacyenv_varas a backward-compatible fallback — no migration
required): theauth_connectwrite path, the provision auth gate
(check_api_key), manifest/configured/eager/lazy/refresh startup-config
resolution, the install-and-run subprocess environment (adoptednpxservers),
and the connect/describe availability checks. Configured.mcp.jsonentries
that match a manifest api-key server inherit the resolved credential (including
dead${VAR}/$VARplaceholders that local stdio env never expands), and the
pmcp secrets check/pmcp initdiagnostics are namespace-aware. (#95)
v1.19.1
Fixed
gateway.describenow exposes nested array/object item schemas. Array and
object arguments were collapsed to a bare"array"/"object"type, hiding
item shape and required item fields, so agents wasted calls guessing at the
payload (e.g.brightdata::search_engine_batchneeds
{"queries": [{"query": "..."}]}).ArgInfogains an optionalitem_schema
with a compact one-level summary, and theinvoke_templateplaceholder now
shows the nested shape (e.g.[{"query": "<string>"}]). Scalar-arg output is
unchanged. (issue #87)index-it-mcpmanifest entry now launches thestdiotransport. The
shipped entry ranserve(the HTTP/admin surface) where PMCP's local process
path needs a stdio MCP child;command/argsand all four install platforms
now end instdio. (issue #89)
Docs
- README pilot config for provisioning
index-it-mcpvia.mcp.jsonwith a
pinned version and the operational env block, noting that per-entryenvis
honored only from.mcp.json(the manifest/overlay schema has noenv:).
v1.19.0
Remediation of the v1.18.0 code review (roadmap specs/phase-plans-v10.md).
Fixed
- Downstream servers now actually recover from failure. A crashed stdio
server never came back:_cleanup_clientcancelled the very connect task
driving the reconnect (agather()cancelling itself →RecursionError).
_cancel_background_tasksnow excludes the current task and_cleanup_client
cancels only the client's own read/stderr tasks. Remote (SSE/HTTP) servers now
auto-reconnect on an unexpected drop (parity with stdio). Failed connects no
longer leave a stale ERROR client or a leaked stderr reader task. Proven by a
real-subprocess integration test. - Transport DoS bounds: pre-session keepalive SSE streams are capped
(PMCP_MAX_KEEPALIVE_STREAMS, default 64) with an absolute lifetime
(PMCP_KEEPALIVE_MAX_SECONDS, default 300); POST bodies are enforced against
the size cap during read so chunked/unadvertised-length bodies can't bypass
it (413), with a request timeout closing slow-trickle connections. - Robustness: terminal task records are evicted past a cap (was unbounded);
disconnect_serverre-cancels pending requests under the lifecycle lock
(orphaned-future window); malformed.mcp.jsonis surfaced (path + reason)
instead of silently disabling its servers;find_project_rootno longer
resolves to$HOME; version-check URLs arequote()-escaped; the two flaky
monitor tests are now deterministic.
Security
- OAuth 2.1 resource-server auth mode and Origin/DNS-rebinding validation are
now reachable. They were fully implemented and tested but never passed to the
running server. New CLI flags/env (--auth-mode,--oauth-issuer,
--oauth-jwks-url,--oauth-audience,--required-scope,--allowed-origin;
PMCP_AUTH_MODE/PMCP_OAUTH_*/PMCP_ALLOWED_ORIGINS) thread them through
GatewayServer. The Origin check now runs by default: a cross-origin browser
Originis rejected (loopback/same-origin/allowlisted pass; no-Origin clients
unaffected). Host validation is opt-in when origins are configured. - Provisioning input validation: a discovered/registered package name is now
validated (rejects leading-dash flag injection, path separators, shell/URL
metachars) before it can reachnpx -y <name>, and the resolved install
command is echoed for confirmation.auth_connectrefuses to persist/export an
env var that isn't the target server's declared credential variable (hard
denylist forLD_*/DYLD_*/NODE_OPTIONS/PATH/PYTHON*), closing an
LD_PRELOAD-into-subprocess code-exec path. - Outbound-fetch hardening: JWKS and auth-metadata fetches no longer follow
redirects to internal hosts; the registry response is size-capped during a
streamed read (no OOM); the private registry endpoint requireshttpsand
rejects link-local/metadata hosts; the registry cache is written atomically at
0600. - Local credentials:
pmcp secrets setreads the value via prompt/--stdin
(never argv/shell history); the secret directory is created0700;
submit_feedbackscrubs the title and all fields (not just the description)
before posting to a public issue.
Changed
- Policy allow/deny matching is now case-sensitive (matching server-ID
semantics elsewhere in the gateway). pmcp statusno longer eagerly connects every server as a side effect of a
read-only query — it reports the lazy view by default; use--probeto connect.- Explicit
--max-concurrent-spawns/--rate-limit/--request-timeoutflags
now take precedence over the matching env var (previously a flag equal to the
default was silently overridden).
v1.18.0
Added
- Private/custom manifest overlay: define your own provisionable MCP servers in
~/.pmcp/manifest.yaml(user),<project>/.pmcp/manifest.yaml(project), or a
file pointed to byPMCP_MANIFEST_PATH, merged over the shipped manifest
without editing it. Precedence is shipped < user < project <PMCP_MANIFEST_PATH
(same-named entries are replaced whole). Overlay parsing is fail-soft — a
missing file is skipped, and a malformed file or a single bad entry logs a
warning and is skipped without crashing the gateway. Merged servers
participate identically ingateway.request_capability,gateway.catalog_search,
gateway.provision, and startuprefreshresolution, gated by the same
policy/auth checks.
v1.17.1
Fixed
- Native Windows gateway startup no longer crashes with
No module named 'fcntl'
(issue #84).acquire_singleton_lock/release_singleton_lockimported the
Unix-onlyfcntlunconditionally; they now take the per-user single-instance
lock viamsvcrt.lockingon Windows andfcntl.flockon POSIX (selected by a
literalsys.platformcheck so type checkers narrow the platform-only
imports). WSL is unaffected. (resourcewas already import-guarded.)
v1.17.0
Fixed
- A single downstream stdout line larger than the read limit (default 10 MiB,
PMCP_STDIO_READ_LIMIT) no longer disconnects the whole server (issue #79,
symptom 1b). The stdout reader now reads in chunks and splits on newlines
itself: an oversized line is dropped — failing only the request it belongs to,
with an actionable "output too large" message — while the connection and other
pending requests stay alive, so the next call no longer fails. Large browser
responses (full-page snapshots, screenshots) were a common trigger of the
reported "session expired"/instability. A reproduction harness lives in
diagnostics/issue-79-1b/. - Downstream tool calls are no longer killed by a fixed wall-clock deadline
(issue #79, symptom 1a).timeout_msis now an inactivity (idle) timeout:
a call survives as long as the downstream MCP server keeps producing output
(including JSON progress notifications, which now count toward per-request
liveness in both the stdio and SSE readers). An absolute backstop caps total
wall-clock time so a chatty-but-never-completing call cannot hang forever —
configurable viaPMCP_REQUEST_CEILING_MS(default 600000ms / 10 min). The
long ceiling applies only to tool invocations; control-plane requests
(initialize, list calls) keep the tighter idle deadline so one stuck server
can't stall startup/refresh. This unblocks legitimately long browser/
automation operations driven through the gateway. - Stdio servers are now spawned in their own session/process group
(start_new_session=True) and reaped as a whole tree on disconnect (issue
#79, symptom 1c). Previously, killing a stdio server (e.g.@playwright/mcp)
left the browser it launched orphaned to init, holding the profile's
SingletonLock and breaking the next launch. A new group-aware
_terminate_process_treehelper (SIGTERM the group, wait, then SIGKILL, with
a single-process fallback when the process is not a group leader, or on
Windows where process groups are unavailable) now backs all four downstream
shutdown paths. gateway.refreshis now diff-based and non-destructive (issue #79, symptom
2). Servers whose resolved config is unchanged are left connected and
running; only servers that were removed or whose config changed are
disconnected, and only newly-added eager servers are connected. Previously
refresh tore down every server and reconnected only the eager set, which
dropped previously-running lazy/provisioned servers to offline (the reported
"105 seen, 0 online") and needlessly respawned unchanged processes (e.g. a
live browser) on every refresh. The diff keeps only servers that are actually
ONLINE — a crashed eager server is reconnected (recovery path preserved);
reconciles the lazy registry to the resolved keep-set so removed/policy-denied
servers can no longer be lazily started; and reconnects a remote server when
its${VAR}auth token has rotated in the env store (compared against the
connect-time resolved headers), instead of keeping stale/revoked auth.- Process-tree reaping now escalates to a group
SIGKILLwhen the leader exits
but a grandchild (e.g. aSIGTERM-ignoring browser) survives theSIGTERM
grace period, and reaps servers concurrently at shutdown so multiple hung
servers can't exceed the shutdown budget and orphan browsers (issue #79/1c).
Added
gateway.catalog_searchwithinclude_offline=truenow surfaces
manifest-only provisionable servers in a dedicatedmanifest_candidatesfield
when the query matches a manifest server by name or keyword but no cached
tools exist yet (issue #78). Candidates carry machine-readable next-action
metadata (provisionable,provision_tool,request_capability_tool,
auth_tool,requires_api_key,api_key_available,env_var) so an agent
can provision the exact server instead of falling back to a plain web search.- Manifest:
brightdatakeywords extended withweb research,current web,
page fetch, andexternal researchso research-oriented prompts match
without the exact brand name.
Changed
gateway.request_capabilitytool description now states it recommends a
server to provision (and thatgateway.provisiondoes the actual install/
start), matching the implementation — it previously claimed to auto-provision
(issue #78).
v1.16.0
Added
- Manifest: 2026 vendor-official servers — Okta (identity/IAM), Zapier
(automation meta-connector), Shopify Dev and Square (commerce), Snowflake
Cortex (data platform), Pinecone (vector DB), Azure DevOps, and the GitLab Duo
remote MCP. Adds Identity, Automation, E-commerce, and Data-Platform discovery
coverage. - Manifest (Tier 2): Elasticsearch, Chroma, Redis, Databricks (managed remote),
Storybook, and Cloudinary — rounding out search/vector, data-platform,
frontend, and media coverage.
Changed
- Manifest:
mongodbnow points at the officialmongodb-mcp-server
(mongodb-js) instead of the communitymongodb-lens, covering Atlas admin in
addition to queries.
v1.15.0
Added
SPEC_COMPLIANCE.mdtracks PMCP against the current stable MCP revision
(2025-11-25) with a per-requirement compliance table, a draft-revision
migration assessment (stateless transport, theio.modelcontextprotocol/tasks
extension,server/discover,CacheableResult, DCR→Client ID Metadata
Documents, SSE resumability), and a next-stable tracking checklist. PMCP is
confirmed compliant with the folded-in current-stable items (403 on invalid
Originper PR #1439,insufficient_scope403 step-up per SEP-835,
input-validation as tool-execution errors per SEP-1303, JSON Schema 2020-12
default dialect per SEP-1613, tool/resource/prompt icon passthrough per
SEP-973).- MCP Registry incremental sync. The registry client persists
last_synced_at
and acceptsupdated_sinceto fetch only changed servers
(?version=latest&updated_since=…with cursor pagination), merging deltas into
the cache viamerge_registry_delta. A failed incremental attempt degrades to
a full fetch, and a failed full fetch degrades to the prior cache. Default
full-fetch callers are unchanged. - Opt-in private-registry support (
PMCP_REGISTRY_ALLOW_PRIVATE, default off).
When enabled withPMCP_REGISTRY_PRIVATE_ENDPOINT, PMCP discovers from a
private/custom registry and tolerates draft/non-GAserver.jsonschema fields,
surfacing all versions — a debugging aid for developers building their own
private MCP servers, not for production discovery. With the flag off,
discovery behavior is unchanged.
v1.14.2
Removed
- Removed the unused BAML/LLM machinery entirely. Outbound LLM calls were retired
long ago in favor of a pure-Python capability router, leaving thebaml-py
dependency, the generatedbaml_client,baml_src/, the retired
llm_summarizerstub, and the deaduse_llm/use_llm_fallbackcode paths as
vestigial. Dropping them removes a heavy dependency (no more native binary) and
the per-release client-regeneration burden. Capability summaries and code
snippets are unchanged: cache → template for summaries, static templates for
snippets. Thellmoptional-dependency extra is gone.