MCP policy enforcement plane — deterministic policy on every Model Context Protocol call: identity, RFC 8707 audience binding, tool schema digest pinning, full audit trail. Python, self-hosted, MIT.
Drift inferred · capture-to-capture
No drift recorded — single capability capture; advisories appear once its surface changes.
transport stdio · streamable-http · http
verified
reported
listed in the official MCP registry counts 0 tools · 0 res
· 0 prompts
permission surface via code analysis
No tools enumerated yet for this server.
prompt-surface
shipped agent-instruction files + hidden-content / dangerous-code findings —
quoted from the analyzed source
analyzed v2.21.0 · analyzer v33 · 2d ago
skills & prompt files 1
- agent-rules mcp_hangar-2.21.0/AGENTS.md
danger signals7
- suspicious endpoint 169.254.169.254 (cloud metadata) mcp_hangar-2.21.0/tests/unit/test_security_identity_and_network.py :35
validate_no_ssrf("http://169.254.169.254/latest/meta-data/") - suspicious endpoint 169.254.169.254 (cloud metadata) mcp_hangar-2.21.0/tests/unit/test_ssrf_provenance.py :140
"http://169.254.169.254/latest/meta-data/", - suspicious endpoint 169.254.169.254 (cloud metadata) mcp_hangar-2.21.0/tests/unit/test_unguarded_config_endpoints.py :46
_, warnings = _run({"meta": {"mode": "remote", "endpoint": "http://169.254.169.254/latest/meta-data/"}}) - over-broad OAuth scope delete_repo mcp_hangar-2.21.0/tests/unit/test_egress_l7.py :30
action, _ = evaluate_tool("delete_repo", rules, ToolAction.DENY) - over-broad OAuth scope delete_repo mcp_hangar-2.21.0/tests/unit/test_egress_l7_enforcement.py :46
_server(L7Policy(tools=ToolRules(deny=("delete_*",)))).invoke_tool("delete_repo", {}) - over-broad OAuth scope delete_repo mcp_hangar-2.21.0/tests/unit/test_set_l7_policy_handler.py :56
server.invoke_tool("delete_repo", {}) - committed secret Google API key mcp_hangar-2.21.0/tests/unit/test_output_redactor.py :237
AIzaSy…(39 chars, redacted)
evidence-backed
findings quoted directly from the published source artifact — not inferred
code files: 1144
filesystem 244
- fs (weak) mcp_hangar-2.21.0/examples/discovery/test_container_discovery.py :21
from pathlib import Path - fs mcp_hangar-2.21.0/fuzz/fuzz_access_precedence.py :19
from pathlib import Path - fs mcp_hangar-2.21.0/fuzz/fuzz_policy_evaluate.py :14
from pathlib import Path - fs mcp_hangar-2.21.0/fuzz/fuzz_policy_parse.py :19
from pathlib import Path - fs (weak) mcp_hangar-2.21.0/scripts/build_changelog.py :42
from pathlib import Path - fs (weak) mcp_hangar-2.21.0/scripts/check_dead_symbols.py :37
import pathlib - fs (weak) mcp_hangar-2.21.0/scripts/check_decision_coverage.py :32
import pathlib - fs (weak) mcp_hangar-2.21.0/scripts/dump_api_routes.py :36
import pathlib - fs (weak) mcp_hangar-2.21.0/scripts/promote_upgrade_notes.py :52
from pathlib import Path - fs (weak) mcp_hangar-2.21.0/scripts/release_please_guard.py :42
from pathlib import Path - fs (weak) mcp_hangar-2.21.0/scripts/smoke_published_artifact.py :43
from pathlib import Path - fs (weak) mcp_hangar-2.21.0/scripts/validate_config.py :20
from pathlib import Path
show 28 more
- fs mcp_hangar-2.21.0/src/mcp_hangar/application/services/secrets_resolver.py :9
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/auth/bootstrap.py :8
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/auth/infrastructure/sqlite_store.py :17
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/auth/infrastructure/sqlite_tap_store.py :10
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/compliance/cef_exporter.py :13
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/compliance/jsonlines_exporter.py :8
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/compliance/leef_exporter.py :7
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/compliance/syslog_exporter.py :9
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/domain/contracts/installer.py :10
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/domain/model/circuit_breaker.py :49
whether to close (on success) or re-open (on failure). - fs mcp_hangar-2.21.0/src/mcp_hangar/domain/services/image_builder.py :6
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/domain/services/task_consent.py :69
def open(self, task_key: TaskKey, input_key: str) -> None: - fs mcp_hangar-2.21.0/src/mcp_hangar/domain/services/tool_access_resolver.py :451
a documented deny list that failed open (#1164). Defaults to - fs mcp_hangar-2.21.0/src/mcp_hangar/facade.py :40
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/fastmcp_server/task_relay_handlers.py :715
consent_gate.open(key, input_key) - fs mcp_hangar-2.21.0/src/mcp_hangar/gc.py :3
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/discovery/docker_source.py :27
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/discovery/filesystem_source.py :28
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/discovery/registry.py :28
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/installers.py :18
import shutil - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/launchers/container.py :18
import shutil - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/launchers/docker.py :3
import shutil - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/launchers/subprocess.py :4
import shutil - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/persistence/backends/sqlite.py :18
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/persistence/database.py :11
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/persistence/database_common.py :11
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/persistence/dispatch_checkpoint.py :11
from pathlib import Path - fs mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/persistence/sqlite_event_store.py :10
from pathlib import Path
204 more not shown — this panel samples each group; the count above is the real total.
shell / exec 76
- shell (weak) mcp_hangar-2.21.0/examples/discovery/test_container_discovery.py :22
import subprocess - shell (weak) mcp_hangar-2.21.0/examples/rugpull/recording/client.py :5
import subprocess - shell (weak) mcp_hangar-2.21.0/scripts/build_changelog.py :44
import subprocess - shell (weak) mcp_hangar-2.21.0/scripts/smoke_published_artifact.py :46
import subprocess - shell mcp_hangar-2.21.0/src/mcp_hangar/domain/services/image_builder.py :8
import subprocess - shell mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/launchers/container.py :19
import subprocess - shell mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/launchers/docker.py :4
import subprocess - shell mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/launchers/subprocess.py :5
import subprocess - shell mcp_hangar-2.21.0/src/mcp_hangar/stdio_client.py :8
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/_hangar_executable.py :24
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/benchmark/tracing_overhead.py :43
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/ci/test_release_pr_checks_are_refired.py :22
import subprocess
show 28 more
- shell (weak) mcp_hangar-2.21.0/tests/ci/test_the_changelog_gate_diffs_from_the_merge_base.py :22
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/ci/test_the_changelog_gate_lets_a_dependency_bump_through.py :14
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/ci/test_the_release_push_is_made_as_the_app.py :24
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/_capability_block_harness.py :27
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_builder_config_takes_effect.py :25
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_caller_error_does_not_count_against_a_group_member.py :25
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_capability_block_stops_the_call_that_found_it.py :29
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_client_over_stdio_gets_a_verdict.py :31
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_cold_group_reports_no_healthy_members_and_still_routes.py :19
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_config_dict_validator_refuses_a_served_call.py :18
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_dead_servers_reason_reads_the_same_on_every_surface.py :25
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_front_door_is_ready_once_its_catalogue_is_projected.py :33
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_front_door_member_of_several_groups_is_governed_by_each.py :28
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_front_door_task_call_is_governed_by_its_group.py :51
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_given_up_server_reads_dead_on_the_served_path.py :42
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_group_member_named_directly_is_governed_on_the_served_app.py :38
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_hot_loaded_server_has_its_log.py :21
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_leftover_circuit_breaker_row_is_ignored.py :20
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_passing_health_check_returns_a_member_to_rotation.py :33
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_reload_is_served_whole.py :22
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_reload_keeps_an_l7_policy_set_over_the_api.py :21
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_reload_restarts_only_what_changed.py :19
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_server_a_reload_adds_has_its_log.py :21
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_a_task_follow_up_is_checked_against_current_tool_access.py :27
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_append_at_end_on_one_postgres.py :24
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_audit_export_switch_bootstrap.py :21
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_audit_log_pipeline_bootstrap.py :25
import subprocess - shell (weak) mcp_hangar-2.21.0/tests/integration/test_each_replica_exposes_its_group_circuit.py :23
import subprocess
36 more not shown — this panel samples each group; the count above is the real total.
network 47
- net (weak) mcp_hangar-2.21.0/examples/auth-keycloak/test_keycloak_integration.py :7
import httpx - net (weak) mcp_hangar-2.21.0/examples/auth-keycloak/test_oidc_local.py :30
import httpx - net (weak) mcp_hangar-2.21.0/scripts/smoke_published_artifact.py :45
import socket - net mcp_hangar-2.21.0/src/mcp_hangar/application/discovery/security_validator.py :27
import aiohttp - net mcp_hangar-2.21.0/src/mcp_hangar/auth/infrastructure/jwt_authenticator.py :490
import httpx - net mcp_hangar-2.21.0/src/mcp_hangar/auth/infrastructure/opa_authorizer.py :15
import httpx - net mcp_hangar-2.21.0/src/mcp_hangar/compliance/syslog_exporter.py :6
import socket - net mcp_hangar-2.21.0/src/mcp_hangar/domain/contracts/authentication.py :123
without the transport-layer concerns of extracting them from requests. - net mcp_hangar-2.21.0/src/mcp_hangar/domain/security/ssrf.py :32
import socket - net mcp_hangar-2.21.0/src/mcp_hangar/domain/value_objects/config.py :19
from urllib.parse import urlparse - net mcp_hangar-2.21.0/src/mcp_hangar/http_client.py :25
import httpx - net mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/launchers/http.py :67
from urllib.parse import urlparse
show 28 more
- net mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/registry/client.py :11
import httpx - net mcp_hangar-2.21.0/src/mcp_hangar/server/api/middleware.py :19
from urllib.parse import parse_qs - net mcp_hangar-2.21.0/src/mcp_hangar/server/bootstrap/__init__.py :24
import socket - net mcp_hangar-2.21.0/src/mcp_hangar/server/cli/commands/add.py :111
import httpx - net mcp_hangar-2.21.0/src/mcp_hangar/server/cli/commands/remove.py :81
import httpx - net mcp_hangar-2.21.0/src/mcp_hangar/server/cli/commands/status.py :53
import httpx - net (weak) mcp_hangar-2.21.0/tests/benchmark/tracing_overhead.py :41
import socket - net (weak) mcp_hangar-2.21.0/tests/integration/_front_door_harness.py :35
import httpx - net (weak) mcp_hangar-2.21.0/tests/integration/test_audit_export_switch_bootstrap.py :20
import socket - net (weak) mcp_hangar-2.21.0/tests/integration/test_audit_log_pipeline_bootstrap.py :24
import socket - net (weak) mcp_hangar-2.21.0/tests/live/conftest.py :24
import socket - net (weak) mcp_hangar-2.21.0/tests/live/test_t0_last_healthy.py :12
import httpx - net (weak) mcp_hangar-2.21.0/tests/live/test_t0_projection_metrics.py :25
import httpx - net (weak) mcp_hangar-2.21.0/tests/live/test_t0_reload_keeps_the_front_door.py :36
import httpx - net (weak) mcp_hangar-2.21.0/tests/live/test_t0_smoke.py :9
import httpx - net (weak) mcp_hangar-2.21.0/tests/live/test_t0_tool_access.py :35
import httpx - net (weak) mcp_hangar-2.21.0/tests/live/test_t2_apikey.py :39
import httpx - net (weak) mcp_hangar-2.21.0/tests/live/test_t2_auth.py :33
import httpx - net (weak) mcp_hangar-2.21.0/tests/live/test_t2_session_suspension.py :48
import httpx - net (weak) mcp_hangar-2.21.0/tests/live/test_t2_tenant_scoped_grants.py :40
import httpx - net (weak) mcp_hangar-2.21.0/tests/unit/test_a_failed_start_closes_its_client.py :29
import socket - net (weak) mcp_hangar-2.21.0/tests/unit/test_events_carry_their_producer.py :135
import socket - net (weak) mcp_hangar-2.21.0/tests/unit/test_jwt_authenticator.py :386
with patch("httpx.get", return_value=mock_response): - net (weak) mcp_hangar-2.21.0/tests/unit/test_opa_authorizer.py :83
import httpx - net (weak) mcp_hangar-2.21.0/tests/unit/test_opa_verdict_is_typed.py :89
import httpx - net (weak) mcp_hangar-2.21.0/tests/unit/test_otlp_audit_exporter.py :399
import socket - net (weak) mcp_hangar-2.21.0/tests/unit/test_sdk_pin_bounds.py :91
httpx 1.0 drops `httpx.AsyncClient`, which the proxy path uses throughout. - net (weak) mcp_hangar-2.21.0/tests/unit/test_session_id_retirement.py :24
"""Return a fake ``httpx.Client.post`` that records the outbound headers."""
7 more not shown — this panel samples each group; the count above is the real total.
secrets 5
- secrets mcp_hangar-2.21.0/src/mcp_hangar/server/api/middleware.py :756
allow_credentials = os.environ.get("MCP_CORS_CREDENTIALS", "false").lower() == "true" - secrets mcp_hangar-2.21.0/src/mcp_hangar/server/bootstrap/__init__.py :558
if os.environ.get("HANGAR_LICENSE_KEY"): - secrets mcp_hangar-2.21.0/src/mcp_hangar/server/bootstrap/observability.py :131
secret_key=os.getenv("LANGFUSE_SECRET_KEY", _expand_env(langfuse_dict.get("secret_key", ""))), - secrets (weak) mcp_hangar-2.21.0/tests/unit/test_bootstrap_components_loading.py :142
assert os.environ.get("HANGAR_LICENSE_KEY") == "hk_v1_test" - secrets (weak) mcp_hangar-2.21.0/tests/unit/test_secrets_resolver.py :96
os.environ["PRECEDENCE_SECRET"] = "env_value"
database 19
- db (weak) mcp_hangar-2.21.0/scripts/migrate_db.py :165
import asyncpg - db mcp_hangar-2.21.0/src/mcp_hangar/auth/infrastructure/sqlite_store.py :19
import sqlite3 - db mcp_hangar-2.21.0/src/mcp_hangar/auth/infrastructure/sqlite_tap_store.py :8
import sqlite3 - db mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/persistence/database_common.py :12
import sqlite3 - db mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/persistence/dispatch_checkpoint.py :12
import sqlite3 - db mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/persistence/sqlite_event_store.py :11
import sqlite3 - db mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/persistence/sqlite_management_lease.py :31
import sqlite3 - db mcp_hangar-2.21.0/src/mcp_hangar/infrastructure/truncation/redis_cache.py :84
import redis - db (weak) mcp_hangar-2.21.0/tests/integration/_leftover_circuit_row_harness.py :26
import sqlite3 - db (weak) mcp_hangar-2.21.0/tests/integration/test_append_at_end_on_one_postgres.py :57
import psycopg2 - db (weak) mcp_hangar-2.21.0/tests/unit/cli/test_auth_bootstrap_admin.py :83
import sqlite3 - db (weak) mcp_hangar-2.21.0/tests/unit/cli/test_the_bootstrap_claim_is_not_spent_on_an_unusable_grant.py :22
import sqlite3
show 7 more
- db (weak) mcp_hangar-2.21.0/tests/unit/test_both_backends_are_peers.py :54
assert "import psycopg2" not in source - db (weak) mcp_hangar-2.21.0/tests/unit/test_event_upcasting.py :6
import sqlite3 - db (weak) mcp_hangar-2.21.0/tests/unit/test_events_are_persisted.py :14
import sqlite3 - db (weak) mcp_hangar-2.21.0/tests/unit/test_postgres_metrics_history_store.py :51
# connection factory, not to this file. A stray `import psycopg2` - db (weak) mcp_hangar-2.21.0/tests/unit/test_replay_does_not_drop_the_approval_gate.py :176
import sqlite3 - db (weak) mcp_hangar-2.21.0/tests/unit/test_sqlite_auth_store.py :112
import sqlite3 - db (weak) mcp_hangar-2.21.0/tests/unit/test_sqlite_tap_store.py :3
import sqlite3
declared dependencies 38
- mcp@==2.0.0
- mcp-types@==2.0.0
- structlog@>=24.0.0
- pydantic@>=2.0.0
- httpx@>=0.25.0,<1
- prometheus-client@>=0.19.0
- pyjwt@>=2.8.0
- cryptography@>=41.0.0
- pyyaml@>=6.0
- docker@>=7.1.0
- aiosqlite@>=0.19.0
- typer@>=0.12.0
show 26 more
- rich@>=13.0.0
- questionary@>=2.0.0
- python-multipart@>=0.0.22
- protobuf@>=6.33.5
- jcs@>=0.2.1
- click@>=8.3.3
- pytest@>=8.0.0
- pytest-cov@>=4.1.0
- pytest-asyncio@>=0.23.0
- pytest-timeout@>=2.2.0
- pytest-benchmark@>=5.1.0
- ruff@==0.16.7
- import-linter@==2.15
- mypy@>=1.8.0
- hypothesis@>=6.90.0
- jsonschema@>=4.20.0
- atheris@>=3.1.0; sys_platform ==
- and@python_version >=
- kubernetes@>=29.0.0
- langfuse@>=2.0.0
- opentelemetry-api@>=1.35.0
- opentelemetry-sdk@>=1.35.0
- opentelemetry-exporter-otlp@>=1.35.0
- psycopg2-binary@>=2.9.9
- redis@>=5.0.0
- fpdf2@>=2.8.0
transport_posture 1
- binds all interfaces (0.0.0.0) mcp_hangar-2.21.0/tests/unit/test_security_critical_paths.py :61
lifecycle.run_http(host="0.0.0.0", port=8000, unsafe_no_auth=False)