MCP-compatible tool registry for 3tears tool system
Project description
3tears Registry
MCP-compatible tool registry for the 3tears tool system. Routes tool calls between agents and tool pods via NATS request/reply.
Part of the 3tears framework.
Components
ToolCatalog-- in-memory index of registered tool pods, backed by a NATS KV bucket for recovery across restarts.RegistrationHandler-- subscribes to{ns}.tools.registerand mutates the catalog.HeartbeatMonitor-- sweeps pods whose heartbeats fell behind the timeout and evicts their endpoints.DiscoveryHandler-- serves{ns}.tools.discoverfor pod-readiness polling.CallProxy-- the hot path. Subscribes to{ns}.tools.call, authorizes viaAgentToolAuthorizer, selects an endpoint via the configuredRoutingStrategy, and forwards the call to the tool pod via NATS request/reply with identity + correlation carried through theCallContextenvelope.RegistryRbacStack-- self-contained rbac surface the standalone server constructs against the connected NATS client: NATS-proxyNamespaceCollection+ four rbac metadata Collections +AclCache+ invalidation subscribers. The_run_server()entry point uses this to wireRbacEvaluatorAuthorizerwithout any host-application loaders, so a standalone server no longer defaults to deny-all.
Authorization
Tool dispatch authorization lives behind the AgentToolAuthorizer protocol. Implementations receive the calling agent id, the invoking user id (from CallContext.user_id), and the fully qualified tool name, and return a boolean decision.
Production deployments wire RbacEvaluatorAuthorizer (in threetears.registry.rbac_authorizer) which delegates to the unified rbac evaluator from threetears.agent.acl:
- The platform-side
ToolNamespaceEmitterlistens on{ns}.tools.registerand upserts aplatform.namespacesrow of typetoolper tool in everyRegistrationManifest. The canonicalnameshape istools.<sanitized-mcp-name>.<sanitized-version>(perbuild_namespace_name);metadatacarries the pre-sanitized natural-identity fieldsmcp_name/mcp_version/pod_idso downstream pattern matching (the access materializer's agent.yamlaccess.toolspatterns) does not need to reverse the sanitization rules. - The authorizer resolves the tool namespace via an injected
NamespaceCollection. The signature isis_authorized(agent_id, user_id, tool_name, tool_version). The implementation builds the canonical lookup key viabuild_namespace_name(PLURAL_PREFIX_TOOL, tool_name, tool_version)rather than passing the rawmcp_namedirectly, so the lookup matches the row the emitter wrote. evaluate_decisionresolves the two-sided grant chain: user side (groups the invoking user is in) intersected with agent side (groups the calling agent is in, short-circuited by namespace ownership). The decision is cached inthreetears.agent.acl.AclCachewith TTL + fine-grained invalidation; cross-process rbac mutations purge the cache promptly via theacl.{membership,assignment,role}.invalidatesubjects theRegistryRbacStacksubscribes to on startup.
Defense in depth: when user_id=None (tool dispatch without user identity) the authorizer denies unconditionally. When the namespace Collection's get_by_name returns None (tool registered but namespace row not yet visible) it denies. This catches registration races rather than defaulting to allow.
Platform-built-in tools land with owner_agent_id=NULL, customer_id=NULL. There is no implicit "anyone can call" behaviour for them. Grants are managed via explicit assignments on the platform-seeded ToolCaller role (same pattern as shared-type workspaces).
RbacEvaluatorAuthorizer is the only authorizer the production server wires: no dual-enforcement window, no back-compat aliases. The declarative access.tools expression on agent.yaml stays as operator-facing syntax and is translated to RBAC assignments at bootstrap.
Dev-mode authorizers
AllowAllAuthorizer permits every dispatch unconditionally, enabled by THREETEARS_REGISTRY_ALLOW_ALL_TOOLS=true. Use only in local dev containers when an explicit RBAC bypass is needed.
DenyAllAuthorizer refuses every dispatch. Available as a panic-button kill switch via THREETEARS_REGISTRY_FORCE_DENY_ALL=true. It is also the millisecond-window placeholder the server holds before the rbac stack is wired against the live NATS client during serve().
Standalone entry point
python -m threetears.registry
Reads THREETEARS_NATS_URL (defaults to nats://localhost:4222) and THREETEARS_NATS_SUBJECT_NAMESPACE (the NATS subject namespace).
By default the entry point wires RbacEvaluatorAuthorizer against a self-contained RegistryRbacStack (NATS-proxy NamespaceCollection + four rbac metadata Collections + AclCache + invalidation subscribers). The proxy collections read through the platform broker's system.platform.rbac carve-out, so no direct DB credentials are needed. The registry is self-sufficient in any deployment with a reachable platform broker. Optional knobs:
THREETEARS_REGISTRY_ALLOW_ALL_TOOLS=true-- bypass the rbac stack entirely (dev only).THREETEARS_REGISTRY_FORCE_DENY_ALL=true-- kill switch for misconfigured deployments.THREETEARS_REGISTRY_ACL_TTL_SECONDS-- override the AclCache TTL (default 60s).
Dispatch flow:
sequenceDiagram
participant Agent
participant Registry
participant RbacStack
participant PlatformBroker
participant ToolPod
Agent->>Registry: ProxyCallRequest(tool_name, tool_version, context.user_id)
Registry->>RbacStack: is_authorized(agent_id, user_id, tool_name, tool_version)
RbacStack->>RbacStack: build_namespace_name(PLURAL_PREFIX_TOOL, tool_name, tool_version)
RbacStack->>PlatformBroker: NamespaceCollection.get_by_name (system.platform.rbac proxy)
PlatformBroker-->>RbacStack: tool namespace row
RbacStack->>RbacStack: evaluate_decision (user ∩ agent grants)
RbacStack-->>Registry: True / False
Registry->>ToolPod: forward call (CallContext echoed)
ToolPod-->>Registry: CallResponse
Registry-->>Agent: ProxyCallResponse
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file 3tears_registry-0.14.1.tar.gz.
File metadata
- Download URL: 3tears_registry-0.14.1.tar.gz
- Upload date:
- Size: 109.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
45e1780373e342fb2e43f0fe9746e1c4ca72c354c699590a92a0ec9f3299ff6b
|
|
| MD5 |
cce953ca3fc2ba7506df466ccd2eea1a
|
|
| BLAKE2b-256 |
71b1f91877217bf32b3f327bfdd5fd175f005ea9d757e740153bfc8eb4fcb7d6
|
File details
Details for the file 3tears_registry-0.14.1-py3-none-any.whl.
File metadata
- Download URL: 3tears_registry-0.14.1-py3-none-any.whl
- Upload date:
- Size: 67.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.1 {"installer":{"name":"uv","version":"0.11.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ec9e3f014c5284af43381439f76076937d34204cec7a39df5bb3fe65879cccd9
|
|
| MD5 |
4a0194fc51d4b222fbac88d44cbf30b9
|
|
| BLAKE2b-256 |
5b670f8287a2d0d8b1c2b9c7d2a15b95bf8591fbfd883b800148b941b1cd68c7
|