Skip to main content
Toolshed acts as an OAuth broker between your agents and third-party services. When a user connects an integration, Toolshed handles the full OAuth flow — authorization redirect, code exchange, token encryption, and storage — and then vends short-lived access tokens to plugin handlers on demand. Your plugin code calls ctx.auth.getToken("provider") and receives a ready-to-use token; it never sees credentials at rest.

Supported providers

Toolshed ships with built-in OAuth support for four providers:

GitHub

Repositories, issues, pull requests, and user data. Scopes: repo, read:user.

Slack

Messages, channels, and workspace data. Scopes: channels:read, chat:write, and more.

Linear

Issues, projects, and teams. Scopes: read, write.

Google Workspace

Gmail, Google Calendar, and Drive. Scopes vary by product.

Initiate an OAuth connection

To connect a user to a provider, redirect them to:
GET /api/auth/:provider/login?userId=<userId>
The userId query parameter is required — it ties the stored token to a specific user in your system. Toolshed redirects the user to the provider’s authorization page, handles the callback, and stores the encrypted token automatically.
# Redirect the user to connect their GitHub account
GET /api/auth/github/login?userId=user_abc123
After the user authorizes, Toolshed stores their token and redirects back with:
{
  "success": true,
  "provider": "github",
  "userId": "user_abc123",
  "scopes": ["repo", "read:user"]
}
In your application UI, generate a link that redirects the user to the Toolshed login endpoint:
function buildConnectUrl(provider: string, userId: string, serverUrl: string): string {
  const params = new URLSearchParams({ userId })
  return `${serverUrl}/api/auth/${provider}/login?${params.toString()}`
}

// Example: "https://my-toolshed.example.com/api/auth/github/login?userId=user_abc123"
const url = buildConnectUrl("github", "user_abc123", "https://my-toolshed.example.com")

List connected providers

After a user has connected integrations, retrieve their active connections:
GET /api/auth/connections?userId=<userId>
curl "https://your-toolshed-server/api/auth/connections?userId=user_abc123"
Response:
{
  "userId": "user_abc123",
  "providers": ["github", "slack"]
}
Use this endpoint to show users which integrations are active and which still need to be connected.

Disconnect a provider

To revoke a user’s connection to a provider, send a DELETE request. Toolshed revokes the token at the provider (best-effort) and removes it from storage:
DELETE /api/auth/:provider?userId=<userId>
curl -X DELETE \
  "https://your-toolshed-server/api/auth/github?userId=user_abc123"
Response:
{
  "disconnected": true,
  "provider": "github",
  "userId": "user_abc123"
}

Using tokens in plugin handlers

Once a user has connected an integration, you do not need to manage tokens yourself. Every tool handler receives a ctx object with an auth resolver — just call ctx.auth.getToken("provider") and Toolshed vends the stored token automatically.
Here is how the GitHub plugin uses this in practice:
async handler(ctx, input) {
  // Toolshed decrypts and vends the stored token — no credentials in your code
  const token = await ctx.auth.getToken("github")

  const res = await fetch(
    `https://api.github.com/repos/${input.owner}/${input.repo}/issues`,
    {
      headers: {
        Authorization: `Bearer ${token}`,
        Accept: "application/vnd.github.v3+json",
      },
    }
  )
  return await res.json()
}
If the user has not connected the required provider, getToken() throws and the tool returns an error to the agent — no partial credentials are leaked.

Declaring required providers

When you build your plugin with definePlugin(), list the OAuth providers your tools depend on in the authProviders array. This lets Toolshed’s UI prompt users to connect before they try to invoke a tool:
definePlugin({
  id: "github",
  name: "GitHub",
  description: "GitHub issues, pull requests, and repository operations.",
  authProviders: [
    {
      id: "github",
      type: "oauth2",
      provider: "github",
      scopes: ["repo", "read:user"],
    },
  ],
  tools: [...],
})
The provider field in authProviders must match the string you pass to ctx.auth.getToken() and the :provider path segment in the auth endpoints.