Skip to content

Connect with External APIs

Use an extension plus the network capability when an agent needs to call an external service.

Create an API Extension

ts
const agent = await app.agents.create({
  title: "API assistant",
  config: {
    instructions: "Use the weather tool when the user asks about weather.",
    capabilities: ["network"],
    extensions: [
      {
        environment: "agent",
        name: "get_weather",
        description: "Fetch current weather for a city.",
        parameters: {
          type: "object",
          properties: {
            city: { type: "string" },
          },
          required: ["city"],
        },
        code: `
export default async function ({ city }) {
  const url = new URL("https://api.example.com/weather");
  url.searchParams.set("city", city);
  const res = await fetch(url);
  if (!res.ok) throw new Error("Weather API failed");
  return await res.json();
}
`,
      },
    ],
  },
});

Use Secrets for API Keys

Do not hardcode API keys, passwords, or authentication tokens in your extension code or prompts. Instead, store them securely as UClaw secrets, and reference them using placeholders inside your code.

1. Register a Secret

Use the app.secrets.add API on your server to save a secret. This persists the credential in UClaw's secure database.

ts
import { AppClient } from "@uclaw/sdk";

const app = new AppClient({
  apiKey: process.env.UCLAW_API_KEY,
});

// Add a secret key (e.g. GitHub API Token), optionally restricting it to specific hosts
await app.secrets.add("GITHUB_TOKEN", "ghp_xxxxxxxxxxxxxxxxxxxx", {
  allowedHosts: ["api.github.com", "*.github.com"],
});

2. Configure Capabilities

Ensure your agent configuration includes both the network and secret capabilities:

ts
const agent = await app.agents.create({
  config: {
    capabilities: ["network", "secret"],
    // ...
  },
});

3. Use Secrets in Extension Code

Use the ${{ secrets.<SECRET_KEY> }} placeholder pattern in your extension's network requests. UClaw's runtime proxy will automatically intercept outgoing requests and replace the placeholder with the actual value securely.

ts
const agent = await app.agents.create({
  config: {
    capabilities: ["network", "secret"],
    extensions: [
      {
        environment: "agent",
        name: "get_repo_stars",
        description: "Get star count of a GitHub repository.",
        parameters: {
          type: "object",
          properties: {
            owner: { type: "string" },
            repo: { type: "string" },
          },
          required: ["owner", "repo"],
        },
        code: `
export default async function ({ owner, repo }) {
  const res = await fetch(\`https://api.github.com/repos/\${owner}/\${repo}\`, {
    headers: {
      // The placeholder will be securely replaced at runtime
      "Authorization": "Bearer \${{ secrets.GITHUB_TOKEN }}",
      "User-Agent": "UClaw-Agent"
    }
  });
  if (!res.ok) throw new Error("GitHub API failed");
  const data = await res.json();
  return { stars: data.stargazers_count };
}
`,
      },
    ],
  },
});

Run the Agent

ts
const run = await agent.run("What is the weather in San Francisco?");
await run.wait({ until: "running", timeoutMs: 60_000 });

for await (const event of run.stream()) {
  if (event.type === "text-delta") process.stdout.write(event.delta);
}