> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runtools.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Tool Hub

> Install, execute, and publish hosted tools

The Tool Hub is where integrations live. Every tool — first-party or one you wrote — runs hosted on `tools.runtools.ai`, resolves its credentials server-side, and is callable the same way whether a person, a script, or an agent makes the call.

It's one registry with three visibility levels: **public** tools appear in the Marketplace, **org** tools are installable across your organization, and **private** tools stay with whoever created them.

## Marketplace

Use the Marketplace to discover public tools, install them for your account, and then attach them to agents or execute them directly.

<CodeGroup>
  ```bash CLI theme={null}
  runtools tool search github
  runtools tool install github
  runtools tool list --installed
  ```

  ```typescript SDK theme={null}
  const results = await rt.tools.search('github');
  await rt.tools.install('github');
  const installed = await rt.tools.list();
  ```

  ```bash cURL theme={null}
  curl https://tools.runtools.ai/v1/marketplace/search?q=github
  curl -X POST https://tools.runtools.ai/v1/tools/github/install \
    -H "X-API-Key: rt_live_xxx"
  ```
</CodeGroup>

In the dashboard, open **Tool Hub** to switch between your custom tools, the Marketplace, and installed tools.

## Credential Resolution

When executing a tool, Runtools fills credentials in this order:

| Priority | Source                                                                              |
| -------- | ----------------------------------------------------------------------------------- |
| 1        | Per-request `credentials`                                                           |
| 2        | Per-request `credentialOverrides` that map credential fields to named secrets       |
| 3        | Stored credentials for the installed tool                                           |
| 4        | Matching user-private or org-shared secrets named for the required credential field |
| 5        | OAuth connection declared by the tool credentials spec                              |

OAuth-enabled tools use Connected Apps:

```bash theme={null}
runtools oauth connect github
runtools oauth status
```

If you are building a product where each customer connects their own tools, use [Hosted Connect](/sdk/hosted-connect) instead. Connected Apps are for the authenticated Runtools user or org. Hosted Connect is for product users such as `atlas + user_123 + gmail`.

```typescript theme={null}
await rt.tools.execute('gmail', {
  action: 'list_emails',
  params: { max_results: 10 },
  credentialScope: 'end_user',
  endUser: {
    appId: 'atlas',
    externalUserId: currentUser.id,
  },
});
```

When a provider has multiple connected accounts, Tool Hub uses the default account automatically. To run against a specific connected account, pass a human-readable selector such as an email or username:

```bash theme={null}
runtools tool exec gmail \
  --action list_emails \
  --account artkara2002@gmail.com
```

API-key tools can store encrypted credentials on the installed tool:

```bash theme={null}
runtools tool credentials customer-db --json '{"apiKey":"crm_xxx"}'
```

## Execute a Tool

<CodeGroup>
  ```bash CLI theme={null}
  runtools tool exec github \
    --action list_repos \
    --params '{}'
  ```

  ```typescript SDK theme={null}
  const result = await rt.tools.execute('github', {
    action: 'list_repos',
    params: {},
  });
  ```

  ```typescript SDK account selector theme={null}
  const result = await rt.tools.execute('github', {
    action: 'list_repos',
    params: {},
    oauth: {
      account: 'alfo19972',
    },
  });
  ```

  ```bash cURL theme={null}
  curl -X POST https://tools.runtools.ai/v1/tools/github/execute \
    -H "X-API-Key: rt_live_xxx" \
    -H "Content-Type: application/json" \
    -d '{"action":"list_repos","params":{},"oauth":{"account":"alfo19972"}}'
  ```
</CodeGroup>

Responses are normalized:

```json theme={null}
{
  "data": {
    "success": true,
    "result": {},
    "durationMs": 123
  }
}
```

## Build your own

Any API becomes a tool with `defineTool()` — a name, a JSON schema for its inputs, and an `execute`:

```typescript tools/customer-api.ts theme={null}
import { defineTool } from '@runtools-ai/sdk';

export default defineTool({
  name: 'customer-api',
  description: 'Look up customer records',
  credentials: {
    required: ['CRM_API_KEY'],
    schema: { CRM_API_KEY: { type: 'string', description: 'CRM API key' } },
  },
  actions: {
    lookup: {
      description: 'Find a customer by email',
      parameters: {
        type: 'object',
        properties: { email: { type: 'string', description: 'Customer email' } },
        required: ['email'],
      },
      execute: async (params, credentials) => {
        const res = await fetch(`https://api.example.com/customers?email=${params.email}`, {
          headers: { Authorization: `Bearer ${credentials.CRM_API_KEY}` },
        });
        return res.json();
      },
    },
  },
});
```

Your `execute` runs in an isolated Deno sandbox: you can `import` `npm:`/`node:` packages and call any public API, but Bun-runtime builtins (`import { SQL } from 'bun'`) don't work and secrets arrive as the `credentials` argument — never from the environment. The full contract, plus credentials and OAuth, is in **[Custom Tools](/sdk/custom-tools)**.

Deploy, then choose who can see it:

```bash theme={null}
runtools deploy --tools-only
runtools tool publish customer-api --org   # org-only · omit --org for the public marketplace (admin) · --unpublish to go private
```

## SDK Methods

| Method                                         | Description                                |
| ---------------------------------------------- | ------------------------------------------ |
| `rt.tools.marketplace()`                       | List public tools                          |
| `rt.tools.search(query)`                       | Search public tools                        |
| `rt.tools.get(slug)`                           | Fetch public tool metadata                 |
| `rt.tools.list()`                              | List installed tools for the caller        |
| `rt.tools.custom()`                            | List visible custom tools                  |
| `rt.tools.install(slug)`                       | Install a tool                             |
| `rt.tools.uninstall(slug)`                     | Uninstall a tool                           |
| `rt.tools.forceUninstall(slug)`                | Admin-only uninstall for every org install |
| `rt.tools.setVisibility(slug, visibility)`     | Set custom-tool visibility                 |
| `rt.tools.storeCredentials(slug, credentials)` | Store user-owned credentials               |
| `rt.tools.credentials(slug)`                   | Check stored credential status             |
| `rt.tools.clearCredentials(slug)`              | Remove stored credentials                  |
| `rt.tools.execute(slug, options)`              | Execute an action                          |

## Best Practices

<AccordionGroup>
  <Accordion title="Use OAuth for user-owned accounts">
    Connected Apps avoid manual token handling and refresh automatically. Use the provider default for normal runs, or pass `oauth.account` / `--account` when a specific email or username should be used.
  </Accordion>

  <Accordion title="Use Hosted Connect for product users">
    If your end users are not Runtools users, create Hosted Connect sessions and run tools with `credentialScope: 'end_user'` plus `endUser.appId` and `endUser.externalUserId`.
  </Accordion>

  <Accordion title="Use secrets for API-key tools">
    Store credentials on the installed tool, pass one-off `credentials`, or map credential fields to named secrets with `credentialOverrides`.
  </Accordion>

  <Accordion title="Test tools directly">
    Run `runtools tool exec` before adding a tool to an agent definition.
  </Accordion>
</AccordionGroup>
