Skip to main content
Sources let you populate the tool registry without writing handler code. You point Toolshed at an existing API — an OpenAPI spec, a GraphQL endpoint, a running MCP server, or a plugin you’ve already built — and it generates tool definitions and invocation logic automatically. All four source types share the same defineSource() entry point from @toolshed/sdk.

Namespaces

Every source requires a namespace. Toolshed prepends that namespace to every tool path the source generates. A billing API with a GET /invoices/list endpoint under namespace "billing" becomes billing.invoices.list in the registry. Namespaces must be a single lowercase identifier. They allow policy patterns like billing.** to grant or restrict access to all tools from that source without naming tools individually.

Source types

OpenAPI

The OpenAPI adapter fetches a JSON or YAML spec, parses every operation, and generates one tool per endpoint. GET/HEAD/OPTIONS operations are treated as read-only; POST/PUT/PATCH/DELETE are marked destructive: true and will trigger elicitation before execution.
import { defineSource } from "@toolshed/sdk"

defineSource({
  type: "openapi",
  id: "billing-api",
  namespace: "billing",
  specUrl: "https://billing.internal.example.com/openapi.json",
  auth: { type: "api_key", header: "X-API-Key", envVar: "BILLING_API_KEY" },
})
Use operationFilter to exclude endpoints you don’t want exposed as tools:
defineSource({
  type: "openapi",
  id: "billing-api",
  namespace: "billing",
  specUrl: "https://billing.internal.example.com/openapi.json",
  auth: { type: "bearer", envVar: "BILLING_TOKEN" },
  operationFilter: ({ method, path }) =>
    method === "GET" && !path.startsWith("/internal"),
})
OpenAPI config fields:
FieldTypeRequiredDescription
idstringYesUnique source identifier
namespacestringYesTool path prefix
specUrlstringYesURL to the OpenAPI JSON or YAML spec
authSourceAuthNoAuth for outbound API calls
baseUrlstringNoOverride the base URL from servers[0] in the spec
operationFilterfunctionNoReturn true to include an operation

MCP

The MCP adapter connects to an existing MCP server and proxies its tools into your registry. Use this to incorporate third-party MCP servers without rewriting them as Toolshed plugins.
defineSource({
  type: "mcp",
  id: "external-mcp",
  namespace: "external",
  transport: {
    type: "http",
    url: "https://mcp.example.com/sse",
    headers: { "X-API-Key": process.env.MCP_API_KEY! },
  },
})
MCP config fields:
FieldTypeRequiredDescription
idstringYesUnique source identifier
namespacestringYesTool path prefix
transportobjectYesHow to connect: http (URL + headers) or stdio (command + args + env)
authSourceAuthNoAuth for the MCP server connection itself

GraphQL

The GraphQL adapter introspects a schema and generates one tool per query and one tool per mutation. Queries are treated as read-only; mutations are marked destructive: true.
defineSource({
  type: "graphql",
  id: "commerce-api",
  namespace: "commerce",
  endpoint: "https://api.example.com/graphql",
  auth: { type: "bearer", envVar: "COMMERCE_TOKEN" },
  headers: { "X-Client-Version": "2" },
})
Filter mutations you don’t want exposed:
defineSource({
  type: "graphql",
  id: "commerce-api",
  namespace: "commerce",
  endpoint: "https://api.example.com/graphql",
  auth: { type: "oauth2", provider: "example", scopes: ["commerce:read"] },
  operationFilter: ({ type }) => type === "query",
})
GraphQL config fields:
FieldTypeRequiredDescription
idstringYesUnique source identifier
namespacestringYesTool path prefix
endpointstringYesGraphQL endpoint URL
authSourceAuthNoAuth for outbound GraphQL calls
headersRecord<string, string>NoExtra headers for introspection and tool calls
operationFilterfunctionNoReturn true to include an operation

Plugin

The plugin source type wraps an existing definePlugin() result so it integrates into the same source registration flow as OpenAPI and GraphQL sources. Use this when you want to register a hand-written plugin through the registry API rather than bundling it directly into the server.
defineSource({
  type: "plugin",
  id: "github-source",
  namespace: "github",
  pluginId: "github",
})
Plugin config fields:
FieldTypeRequiredDescription
idstringYesUnique source identifier
namespacestringYesTool path prefix
pluginIdstringYesThe id of the definePlugin() result to wrap

Source authentication

All source types except plugin accept an auth field that controls how the adapter authenticates outbound requests to the upstream API.
Delegates token acquisition to a named OAuth2 provider. Scopes are optional and can refine what the token grants.
auth: { type: "oauth2", provider: "github", scopes: ["repo"] }
Reads a key from an environment variable and sends it in a named request header.
auth: { type: "api_key", header: "X-API-Key", envVar: "MY_API_KEY" }
Reads a token from an environment variable and sends it as Authorization: Bearer <token>.
auth: { type: "bearer", envVar: "MY_TOKEN" }
No authentication. Use for public APIs or APIs where auth is handled by a proxy layer.
auth: { type: "none" }

Registering a source via the API

Once you have a defineSource() config, register it by sending it to POST /api/registry/sources. Toolshed resolves the source, generates its tools, and adds them to the catalog immediately.
curl -X POST https://your-toolshed-server/api/registry/sources \
  -H "Content-Type: application/json" \
  -d '{
    "type": "openapi",
    "id": "billing-api",
    "namespace": "billing",
    "specUrl": "https://billing.internal.example.com/openapi.json",
    "auth": { "type": "api_key", "header": "X-API-Key" }
  }'
A successful response returns the source ID and the list of tools that were generated:
{
  "registered": true,
  "sourceId": "billing-api",
  "name": "Billing API",
  "toolCount": 12,
  "tools": [
    { "path": "billing.invoices.list", "name": "List Invoices", "destructive": false },
    { "path": "billing.invoices.create", "name": "Create Invoice", "destructive": true }
  ]
}
The operationFilter field is a function and cannot be serialized to JSON. If you need filtering, configure it in code using defineSource() and register the source programmatically rather than via the REST API.
To remove a source and deregister all its tools:
curl -X DELETE https://your-toolshed-server/api/registry/sources/billing-api