Most agents aren’t in an MCP host. They still shouldn’t hold your keys.

A LangChain tool, a scheduled job, a worker that reconciles invoices at 3am. None of them speak MCP, and all of them tend to end up with a token in an environment variable. The client libraries are the same broker, reached from ordinary code: your process proves who it is, and AgentValet holds everything else.

Livelast reviewed 24 September 2026

One package, no server to run

Node and Python are ports of each other: same endpoints, same approval behaviour. Node 18 or later, or Python 3.9 or later.

  1. Install the client for your language.

    Terminal
    npm install @agentvalet/client
    # or
    pip install agentvalet
  2. Register the agent. The command prints AGENT_ID, OWNER_ID, PROXY_URL and AGENT_PRIVATE_KEY_PATH when it finishes; set them where your code runs. From Python without Node, agentvalet register --code <code> does the same with an invite or enrolment code, and keeps the key in ~/.agentvalet/agent.key.

    Terminal
    npx @agentvalet/register
  3. Grant platforms and scopes in the dashboard. What you grant is exactly what this agent can reach.

  4. Make a call. You name the platform, the endpoint and the scope; AgentValet checks it, decrypts the real credential in memory, makes the call and writes the audit row.

    notify.ts
    import { AgentValet } from "@agentvalet/client";
    
    const av = AgentValet.fromEnv();
    
    const result = await av.call({
      platform: "slack",
      endpoint: "/api/chat.postMessage",
      method: "POST",
      scope: "chat:write",
      data: { channel: "#general", text: "Deploy finished." },
    });
The same call in Python
  1. reason is shown to whoever approves, if this scope needs approval.

    notify.py
    from agentvalet import AgentValet
    
    with AgentValet.from_env() as av:
        result = av.call(
            platform="slack",
            endpoint="/api/chat.postMessage",
            method="POST",
            scope="chat:write",
            data={"channel": "#general", "text": "Deploy finished."},
            reason="Notify the team that the deploy completed",
        )
Where the client finds its key
  1. In order: AGENT_PRIVATE_KEY_B64, AGENT_PRIVATE_KEY_PATH, AGENT_PRIVATE_KEY, then ~/.agentvalet/agent.key. Ids come from AGENT_ID and OWNER_ID (an AGENTVALET_ prefix wins if both are set). In a container, mount the key or pass it base64-encoded; nothing else needs to travel with your agent.

What’s governed here

Nothing is allowed until you grant it. This is a typical first grant for Your own code; you decide every row, and you can change it without touching the agent.

Example grant for Your own code. Platforms and actions are illustrative.
What the agent triesPlatformDecision
Post a deploy noticeSlackAllowed within grant
Read invoicesXeroAllowed within grant
Void an invoiceXeroAsks you
Anything you haven’t grantedAnyNot granted

A human in the loop is just a slower function call.

Mark a scope as needing approval and your code doesn’t change. The broker holds the action, your call() waits, you approve from your phone, and the result comes back. From your program’s point of view it simply took longer.

If nobody answers in time you get ApprovalTimeoutError, and that isn’t a failure: the action is still queued. Keep the approval id and resume with waitForApproval(approvalId) later, from this process or another one.

Governance you can’t handle in code is just an outage.

Every failure is a typed error, so your agent can tell a refusal from a Slack outage from a person saying no, without matching strings.

AccessDeniedError
No grant for this platform and scope, or your policy blocked it. Recoverable: requestAccess() asks an admin and waits for the answer.
ApprovalDeniedError
A person looked at this action and said no. The one error your agent should never retry.
ApprovalTimeoutError
You stopped waiting; the action didn’t. Still queued, resumable from any process.
UpstreamError
Allowed, run, and the platform itself returned an error.
ConfigError
Missing or malformed identity or key, raised before any network call.
NetworkError
The connection failed, with a hint pointing at DNS, TLS interception, a firewall or a timeout.

Node and Python SDK referenceThe longer walkthrough, kept with the product docs.

All integrations or the platforms your agents can reach

Put Your own code behind AgentValet.

Register the agent, grant only what it needs, and approve the risky calls from your phone. Free to start, no credit card.