How Skill Bridging Is Implemented and Exposed via MemoryProxy in TencentDB-Agent-Memory

The MemoryProxy implements a skill-bridge reverse-proxy that lets large language models (LLMs) invoke the core Skill API without exposing bearer tokens or allowing identity spoofing, using multi-layer session validation, write-access controls, and version pinning.

The skill bridging architecture in TencentDB-Agent-Memory creates a secure boundary between LLM agents and the core skill subsystem. By mounting a dedicated handler at /skill-bridge/*, the proxy intercepts LLM requests, injects tenant identity metadata, enforces security policies, and forwards sanitized payloads to the core skill endpoint.

Endpoint Registration and Routing

The bridge registers as a Hono HTTP handler through createSkillBridgeHandler and mounts at the path /skill-bridge/* in the proxy server.

In MemoryProxy/src/server.ts (lines 124-130), the route is established:

// MemoryProxy/src/server.ts
import { createSkillBridgeHandler } from "./skill/skill-bridge.js";

export function startProxy(config: ProxyConfig) {
  const app = new Hono();
  // … other routes …
  const skillBridge = createSkillBridgeHandler(config);
  app.post("/skill-bridge/*", (c) => skillBridge(c));
  return app;
}

The createSkillBridgeHandler factory function (lines 44-50 in MemoryProxy/src/skill/skill-bridge.ts) returns an async handler that processes all incoming skill requests.

Request Validation and Sub-Path Whitelisting

Incoming requests must satisfy strict validation rules before reaching the core skill API.

Method and content-type enforcement: The bridge only accepts POST requests with application/json content types.

Path validation: The handler checks the request sub-path against an ALLOWED_SUBPATHS set (lines 38-58). Only specific operations—such as search, list, create, update, get, and files/read—are permitted. Any undefined sub-path returns an immediate error.

Session Identity Extraction and Validation

The bridge resolves the caller's identity without trusting client-provided bearer tokens, instead deriving authorization from session identifiers.

L1 and L2 Session Lookup

Session extraction occurs in MemoryProxy/src/skill/skill-bridge.ts (lines 34-42 and 94-106):

  1. Header extraction: The bridge reads session identifiers from headers x-conversation-id, x-session-id, x-chat-id, or x-thread-id.
  2. L1 cache lookup: It first queries an in-memory L1 map via loadSessionIdsL1 for hot session data.
  3. L2 binding lookup: On L1 miss, it falls back to loadSessionIdsL2, which reads flattened binding JSON stored per-tenant (spaceId).

The resulting SessionIdFields object contains:

  • user_id
  • team_id
  • agent_id
  • space_id
  • user_key
  • A composite key for telemetry

These fields are immutable for the request lifecycle and enforce strict multi-tenant isolation.

Write Access Control and Security Policies

The proxy can disable LLM-initiated mutations using the config.skillRuntime?.allowLlmWrite flag. When writes are disabled and the sub-path exists in WRITE_SUBPATHS (e.g., create, update, files/write), the bridge returns HTTP 403 Forbidden (lines 60-66).

This safety mechanism prevents autonomous agents from modifying skills unless explicitly permitted by the deployment configuration.

Payload Construction and Core Forwarding

After validation and identity extraction, the bridge constructs the outbound payload (lines 91-101 and 144-150):

const outbound = {
  ...inboundBody,           // Original LLM payload
  team_id: ids.team_id,    // Injected from session
  agent_id: ids.agent_id,  // Injected from session
  user_id: ids.user_id,    // Injected from session
};

The request forwards to ${config.coreSkill.endpoint}/v3/skill/${sub} with the service authentication token added by the proxy. The core skill router validates the injected identity fields to enforce tenant isolation at the destination.

Version Pinning and Optimistic Locking

The bridge implements version pinning to ensure LLMs operate against consistent skill snapshots.

Read operations (get, files/read): The handler injects a version field obtained from VersionPinRepo (backed by Redis via version-pin-repo.ts or COS via kv-version-pin-repo.ts). This pins the LLM to a specific skill version.

Write operations (update, patch, files/write, files/remove): The bridge injects expected_version to enable optimistic locking, preventing concurrent modification conflicts.

The resolution logic in resolveBacking (lines 98-106) determines the appropriate version for each request type.

Team-Wide Search and Visibility Whitelisting

When handling the search sub-path, the bridge composes a visibility whitelist through three data sources (lines 140-156 and 160-170):

  1. Team-visible skills: Calls the meta service via list-accessible to fetch skills visible to the entire team.
  2. Agent-owned skills: Queries the core API for skills owned by the calling agent (including private skills).
  3. Session-injected skills: Lists skills already present in the current conversation context.

The final whitelist formula is (A ∪ B) - C, removing any skills already injected into the session to avoid duplication. If the whitelist is empty, the bridge short-circuits with an empty result set.

The bridge fetches a maximum of PLUGIN_SEARCH_HARD_TOPK = 50 results, then filters the response via filterTeamSearchResponse to return only whitelisted items matching the original top_k parameter.

Telemetry and Observability

Every upstream call emits a bridge-tool-call telemetry event via emitBridgeToolCallTelemetry (lines 83-90 and 132-140). The event records:

  • Session key and tenant identifiers
  • Target endpoint and sub-path
  • Request payload size
  • HTTP status code
  • Latency metrics

This observability layer operates regardless of request success or failure, providing complete audit trails for LLM-skill interactions.

LLM Integration via Tool Injection

The proxy exposes the bridge to LLMs through prompt injection. The skill-tools-injector.ts file (lines 66-70) generates a <skill_tools> block that instructs the LLM to use Bash curl commands against the bridge endpoint:

以下是云端 skill 操作工具。**这些不是本地工具**,需要用 Bash 调用 curl 命中 proxy 的 skill-bridge 路径来执行。

 skill_view <skill_id>  # 查看 skill  

 skill_patch <skill_id> <patch>  # 更新 skill  

The injector constructs URLs using the pattern ${base}/skill-bridge/v3/skill, ensuring the LLM targets the proxy rather than the core API directly.

Summary

  • Skill bridging creates a secure reverse-proxy layer between LLMs and the core skill API, preventing token exposure and identity spoofing.
  • Session resolution uses a two-tier lookup (L1 memory map → L2 tenant binding) to extract immutable identity fields from conversation headers.
  • Access control enforces sub-path whitelisting (ALLOWED_SUBPATHS) and optional write-blocking (WRITE_SUBPATHS) to restrict LLM capabilities.
  • Version management pins reads to consistent snapshots and applies optimistic locking (expected_version) to write operations.
  • Team search composes visibility whitelists by unioning team-visible and agent-owned skills, then subtracting already-injected session skills.
  • Observability emits structured telemetry for every bridge invocation, tracking latency, payload sizes, and outcomes.

Frequently Asked Questions

How does the MemoryProxy prevent an LLM from spoofing another user's identity?

The MemoryProxy extracts session identifiers from headers like x-conversation-id or x-session-id, then performs L1/L2 lookups to resolve the actual user_id, team_id, and agent_id from authoritative binding stores. These identifiers are injected into the outbound request by the proxy itself, overwriting any values provided by the LLM, ensuring the core API always receives cryptographically verified identity fields.

What happens if an LLM attempts to modify a skill when writes are disabled?

When config.skillRuntime.allowLlmWrite is set to false, the bridge inspects the requested sub-path against WRITE_SUBPATHS. If the operation is a mutation (e.g., create, update, files/write), the handler returns an HTTP 403 Forbidden response immediately, preventing the request from reaching the core skill endpoint.

How does version pinning work for concurrent skill access?

For read operations, the bridge queries the VersionPinRepo to retrieve a pinned version identifier for the specific (space_id, user_id, agent_source, session_key, skill_id) tuple, injecting it as the version parameter. For writes, it injects expected_version to enable optimistic locking; if the skill has been modified since the pin was established, the core API rejects the update, preventing lost updates.

Why does the team-wide search fetch 50 results when the LLM requests fewer?

The bridge sets PLUGIN_SEARCH_HARD_TOPK = 50 to fetch a superset of potential matches from the core API. It then applies filterTeamSearchResponse to remove non-whitelisted items and limit results to the original top_k requested by the LLM. This pattern ensures accurate visibility filtering while minimizing round-trips to the core service.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →