Developer reference
Developer docs
Connect an agent, check your account, then discover the tools you can use.
1. What Valet is
Valet connects your agent to your company’s apps through one MCP address. It checks your company’s rules before each app action and records the outcome.
Use Claude’s browser sign-in without a key, or create a personal key on API keys and send it as Authorization: Bearer vk_....
Keys act as you in the selected company and follow the same rules and approvals. Copy a new key when it is shown, keep it private, and revoke it on the API keys page when it is no longer needed.
2. Quick start
- Sign up with your email and create a company.
- As an administrator, open Apps. Enable catalog apps, add an app from the directory, or add a custom app using its MCP address. Complete its connection steps. New MCP tools need review before use.
- Set Rules for who may use each app and which actions need approval. Members can ask their administrator to do this.
- Connect your agent below. Call
WHOAMIto check your company and role, thenSEARCH_TOOLSto find an action. Follow any personal connection link before retrying that action.
3. Connect your client
MCP address: https://valet.getsentiense.com/mcp. Replace vk_... with your own Valet key.
Claude Code
Run this command, then open /mcp in Claude Code and sign in to Valet in your browser.
claude mcp add --transport http valet https://valet.getsentiense.com/mcp
Or connect with an API key:
claude mcp add --transport http valet https://valet.getsentiense.com/mcp --header "Authorization: Bearer vk_..."
Cursor
Add this to .cursor/mcp.json in your project, or ~/.cursor/mcp.json for your user. Keep files containing keys out of source control.
{
"mcpServers": {
"valet": {
"url": "https://valet.getsentiense.com/mcp",
"headers": {
"Authorization": "Bearer vk_..."
}
}
}
}
OpenAI Responses API
Install openai. Set OPENAI_API_KEY, OPENAI_MODEL to an MCP-capable model you can use, and VALET_API_KEY in your environment. The Valet address must be reachable from OpenAI.
import os
from openai import OpenAI
response = OpenAI().responses.create(
model=os.environ["OPENAI_MODEL"],
input="Call WHOAMI and tell me which company I am connected to.",
tools=[{
"type": "mcp",
"server_label": "valet",
"server_url": "https://valet.getsentiense.com/mcp",
"headers": {"Authorization": "Bearer " + os.environ["VALET_API_KEY"]},
"allowed_tools": ["WHOAMI"],
"require_approval": "never",
}],
)
print(response.output_text)
This example exposes only WHOAMI and skips OpenAI’s separate approval step for that account check. Valet’s app rules and approvals still apply when you expose other tools.
OpenAI Agents SDK
Install openai-agents and set the same three environment variables. This example connects from your Python process.
import asyncio
import os
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp
async def main():
async with MCPServerStreamableHttp(
name="valet",
params={
"url": "https://valet.getsentiense.com/mcp",
"headers": {"Authorization": "Bearer " + os.environ["VALET_API_KEY"]},
},
) as server:
agent = Agent(
name="Account check",
model=os.environ["OPENAI_MODEL"],
instructions="Call WHOAMI and report the company and role.",
mcp_servers=[server],
)
result = await Runner.run(agent, "Which company am I connected to?")
print(result.final_output)
asyncio.run(main())
Python MCP client
Install mcp==2.3.0 (includes httpx2) and set VALET_API_KEY. This calls the tool directly.
import asyncio
import os
import httpx2
from mcp import ClientSession
from mcp.client.streamable_http import streamable_http_client
async def main():
headers = {"Authorization": "Bearer " + os.environ["VALET_API_KEY"]}
async with httpx2.AsyncClient(headers=headers) as http_client:
async with streamable_http_client("https://valet.getsentiense.com/mcp", http_client=http_client) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("WHOAMI", arguments={})
print(result)
asyncio.run(main())
Raw JSON-RPC with curl
Save this as a Bash script, replace the key, and run it with bash. It needs curl and awk. Responses may contain SSE data: lines.
The example uses protocol version 2025-11-25 for the initialize flow. Valet is stateless and currently returns no session ID; if a server returns Mcp-Session-Id, the script sends it on both following requests.
#!/usr/bin/env bash
set -euo pipefail
MCP_URL='https://valet.getsentiense.com/mcp'
VALET_API_KEY='vk_...'
DOCS_TMP=$(mktemp -d)
trap 'rm -rf "$DOCS_TMP"' EXIT
# 1. Initialize using a supported Streamable HTTP protocol version.
curl --fail --silent --show-error --no-buffer --max-time 30 "$MCP_URL" \
-H "Authorization: Bearer $VALET_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-D "$DOCS_TMP/headers" -o "$DOCS_TMP/initialize" \
--data '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"valet-docs","version":"1.0.0"}}}'
cat "$DOCS_TMP/initialize"
# Preserve a session ID if returned. Valet currently serves stateless requests.
SESSION_ID=$(awk 'tolower($1) == "mcp-session-id:" {gsub("\r", "", $2); print $2}' "$DOCS_TMP/headers")
SESSION_HEADERS=(-H 'MCP-Protocol-Version: 2025-11-25')
if [ -n "$SESSION_ID" ]; then
SESSION_HEADERS+=(-H "Mcp-Session-Id: $SESSION_ID")
fi
# 2. Finish initialization. A successful notification has no response body.
curl --fail --silent --show-error --no-buffer --max-time 30 "$MCP_URL" \
-H "Authorization: Bearer $VALET_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
"${SESSION_HEADERS[@]}" \
--data '{"jsonrpc":"2.0","method":"notifications/initialized"}'
# 3. Check your account. The result contains person, tenant and role.
curl --fail --silent --show-error --no-buffer --max-time 30 "$MCP_URL" \
-H "Authorization: Bearer $VALET_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
"${SESSION_HEADERS[@]}" \
--data '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"WHOAMI","arguments":{}}}'
4. The six tools
WHOAMI- Your person, company, role, discoverable toolkits, and discovery decision counts.
SEARCH_TOOLS- Find available tools for your use case.
GET_TOOL_SCHEMAS- Get input schemas for available tools.
EXECUTE- Execute discovered tools. Each call returns its own status.
MANAGE_CONNECTIONS- Add or remove your personal app connections, or list connection statuses.
WAIT_FOR_CONNECTIONS- After the user confirms connecting, wait for any or all requested toolkits to connect or reach a terminal failure.
5. Rules and approvals
Read actions retrieve data. Write actions create or change data, including sending messages. Destructive actions delete data or make changes that may be hard to undo.
Your administrator can allow, block, or require approval for an action. When approval is needed, EXECUTE returns approval_required, an approval_url and an approval_id. The action has not run.
Show the approval link to the person. After approval, resend the identical tool and arguments with that approval_id. Changed arguments need a new approval. Never retry a denied call.
If a call returns needs_connection, show its connection link or administrator message. After the person confirms they connected, use WAIT_FOR_CONNECTIONS, then retry when connected.
6. Pass-through tools
Some catalog apps provide three tools for their REST API. Find their exact names with SEARCH_TOOLS and fetch their schemas before calling them through EXECUTE.
<APP>_API_READsends GET requests.<APP>_API_WRITEsends POST, PUT or PATCH, selected withmethod.<APP>_API_DELETEsends DELETE and counts as destructive.
Supply a path starting with /, up to 2048 characters. Put query parameters in query, with string, number or boolean values. Write and delete tools can include a JSON body.
Requests stay on the app’s configured API addresses. If there is more than one, origin selects its zero-based index. You cannot supply a host, custom headers, path traversal, query or fragment delimiters in the path, or method overrides. Valet supplies the connected app’s credentials.
Rules apply to the whole pass-through tool, not individual paths. GET is classified as read, POST/PUT/PATCH as write and DELETE as destructive; check the app’s API reference for what an endpoint does. The request, response and execution limits below also apply.
7. Limits
These are this server’s configured values, using the defaults when no override is set. Browser and MCP request budgets are separate. Rate and concurrency caps apply per web process.
- MCP requests per IP per minute
- 600
- MCP requests per person per minute
- 120
- MCP requests per company per minute
- 1200
- Page requests per IP per minute
- 600
- Signed-in page requests per person per minute
- 120
- Signed-in page requests per company per minute
- 1200
- Sign-in requests per IP per minute
- 20
- Concurrent MCP requests per person
- 8
- Concurrent executions per person across batches
- 8
- Calls per EXECUTE batch
- 10
- Concurrent calls within a batch
- 5
- Call timeout (seconds)
- 45
- Batch deadline (seconds)
- 180
- MCP request body (bytes)
- 1048576
- Upstream response (bytes)
- 5242880
- Result text (characters)
- 100000
- Search queries per call
- 7
- Schemas per call
- 30
- Connection wait (seconds)
- 120
- Pending approvals per person
- 20
- Approval lifetime (seconds)
- 1800
- Active API keys per person
- 10
- Active custom apps per company (directory apps excluded)
- 50
HTTP 429 means a request or concurrency limit was reached. Wait before retrying; use Retry-After when present. A failed or timed-out write may already have reached the app, so check its outcome before retrying.