NIN Connect Docs
Providers

Claude Agents SDK

Use NIN Connect tools with the Anthropic Claude API — tool_use, manual agent loop, and input_schema mapping.

Installation

npm install @nin.in/core @anthropic-ai/sdk

Environment Variables

NIN_API_KEY=nk_live_xxxxxxxxx
ANTHROPIC_API_KEY=sk-ant-xxxxxxxxx

Full Example

import { Nin } from "@nin.in/core";
import Anthropic from "@anthropic-ai/sdk";

const nin = new Nin({ apiKey: process.env.NIN_API_KEY! });
const session = nin.session("user_123");
const anthropic = new Anthropic();

// fetch tool definitions from the Slack plugin
const toolDefs = await session.getTools({ plugins: ["slack"] });

// map to Anthropic tool format
// inputSchema maps directly to input_schema, so Claude sees every
// parameter name, type, and description
const tools = toolDefs.map((t) => ({
  name: t.slug,
  description: t.description,
  input_schema: t.inputSchema ?? {
    type: "object" as const,
    properties: {},
  },
}));

// run an agentic loop
let messages: Anthropic.MessageParam[] = [
  { role: "user", content: "Send 'hello team' to #general on Slack" },
];

let response = await anthropic.messages.create({
  model: "claude-sonnet-4-20250514",
  max_tokens: 1024,
  tools,
  messages,
});

// handle tool calls in a loop
while (response.stop_reason === "tool_use") {
  const toolUse = response.content.find((b) => b.type === "tool_use");
  if (!toolUse || toolUse.type !== "tool_use") break;

  // execute via NIN — auth is injected automatically
  const result = await session.execute(
    toolUse.name,
    toolUse.input as Record<string, unknown>
  );

  messages = [
    ...messages,
    { role: "assistant", content: response.content },
    {
      role: "user",
      content: [
        {
          type: "tool_result",
          tool_use_id: toolUse.id,
          content: JSON.stringify(result),
        },
      ],
    },
  ];

  response = await anthropic.messages.create({
    model: "claude-sonnet-4-20250514",
    max_tokens: 1024,
    tools,
    messages,
  });
}

console.log(response.content);

How It Works

  1. session.getTools() returns tool definitions with inputSchema (JSON Schema)
  2. Tools are mapped to Anthropic's format: { name, description, input_schema }
  3. The first messages.create() call sends the prompt and tools to Claude
  4. When Claude returns stop_reason: "tool_use", extract the tool call
  5. session.execute() runs the tool through NIN with auth injected
  6. The tool result is appended as a tool_result message
  7. Loop until Claude produces a final text response

Agent Loop Helper

For cleaner code, extract the loop into a reusable function:

const runAgent = async (
  prompt: string,
  session: ReturnType<Nin["session"]>,
  pluginSlugs: string[]
) => {
  const toolDefs = await session.getTools({ plugins: pluginSlugs });
  const tools = toolDefs.map((t) => ({
    name: t.slug,
    description: t.description,
    input_schema: t.inputSchema ?? {
      type: "object" as const,
      properties: {},
    },
  }));

  let messages: Anthropic.MessageParam[] = [
    { role: "user", content: prompt },
  ];

  while (true) {
    const response = await anthropic.messages.create({
      model: "claude-sonnet-4-20250514",
      max_tokens: 1024,
      tools,
      messages,
    });

    if (response.stop_reason !== "tool_use") {
      return response.content;
    }

    const toolUse = response.content.find((b) => b.type === "tool_use");
    if (!toolUse || toolUse.type !== "tool_use") return response.content;

    const result = await session.execute(
      toolUse.name,
      toolUse.input as Record<string, unknown>
    );

    messages = [
      ...messages,
      { role: "assistant", content: response.content },
      {
        role: "user",
        content: [
          {
            type: "tool_result",
            tool_use_id: toolUse.id,
            content: JSON.stringify(result),
          },
        ],
      },
    ];
  }
};

// usage
const output = await runAgent(
  "Send 'hello team' to #general on Slack",
  session,
  ["slack"]
);

On this page