An MCP server in 5 minutes
The goal: your agent calls an API (OpenAI, Stripe, GitHub…) without ever holding the key. Keyward stores the key, and your agent calls the provider through Keyward — which injects the real key server-side and records every call. No key in your .env, ever.
Everything below is on the hosted dashboard at www.keyward.dev plus about six lines of code. You do not need to run anything locally, and — for a plain agent or MCP server — you do not need to wire up user logins.
Store your provider key
In the console, go to Secrets → Connect a provider. Pick a provider from the dropdown (e.g. OpenAI), paste your real API key, and choose the scopes it may be used for. Keyward encrypts it — it's never shown again and never returned by the API. This is the last time that key touches anything but Keyward. Every provider's full scope list lives in the Provider reference.
Register your agent and get its key
Go to Agents → Register an agent and give it an id (e.g. my-mcp). Then click its service key button — Keyward shows you a kw_svc_… key once. Copy it into your MCP server's environment. This key is how the agent proves it's allowed to act — it replaces the provider key you used to hardcode.
KEYWARD_URL=https://broker.keyward.dev
KEYWARD_AGENT=my-mcp
KEYWARD_AGENT_KEY=kw_svc_... # shown once, from the Agents pageAllow it (once)
Keyward is deny-by-default. On Policies, pick your agent, provider, and scope from the dropdowns and add an allow rule (a fresh org starts with one wildcard allow, so you can skip this while trying it out). This is the one governance step — after it, the agent can only ever do what you allowed here. How the two permission layers relate is explained in Concepts.
Call the API through Keyward
Install the SDK and route your provider calls through the gateway. Instead of holding an OpenAI client with a key, you call kw.gateway(...) — same request, but the key lives in Keyward, not your code.
npm add @keyward.dev/sdkimport { createClient } from "@keyward.dev/sdk";
const kw = createClient({
controlPlaneUrl: process.env.KEYWARD_URL,
agent: process.env.KEYWARD_AGENT,
agentKey: process.env.KEYWARD_AGENT_KEY, // the agent acts as itself — no user token
});
// Anywhere you'd normally call OpenAI directly:
const res = await kw.gateway("openai", "/v1/chat/completions", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ model: "gpt-4o", messages: [{ role: "user", content: "hi" }] }),
});
const data = await res.json(); // the real OpenAI responseWatch it happen
Open Audit log. Every call — allowed or denied — is right there, tied to the agent that made it, with the provider, scope, and decision. That's the whole point: your keys never leave Keyward, and every use is accountable.
When there's a real human in the loop
The service key above is for agents acting on their own (MCP servers, background jobs). When a specific person authorized the action and you want it traced to them, pass that user's identity token instead of the service key — Keyward then records the full user → agent chain. Same call, richer attribution:
const kw = createClient({
controlPlaneUrl: process.env.KEYWARD_URL,
agent: "my-mcp",
token: endUserIdentityToken, // instead of agentKey
});That's the only difference. Start with the service key to get running; add user tokens when you need per-person attribution. Full SDK options are in TypeScript SDK.