Skip to main content
Custom Tools let you bring your organization’s internal tools onto Tamarind and use them everywhere the platform already goes: the web UI, the REST API, batch workflows, pipelines, and the Agent / MCP server. You develop the tool; Tamarind handles scaling, infrastructure, and sharing with your teammates. Manage your tools at app.tamarind.bio/app/custom-tools. Code Editor

Overview

Each custom tool has four tabs:
  • Code — edit source files, view previously deployed versions, and inspect build logs.
  • Test — run any version of the tool against test inputs from the browser.
  • Config — manage the tool’s display metadata, compute settings, and input schema.
  • Settings — configure the GitHub integration and admin options.
Once deployed, a custom tool shows up alongside Tamarind’s built-in tools and can be invoked from the UI, the API, Batch, Pipelines, and the MCP server.

Versioning

Each tool is versioned with consecutive integers. Click any previously deployed version to load it in the code editor. Editing files and clicking Deploy builds a new version. Each version carries its own input schema, which you can edit from the Config tab. This means older versions remain runnable with the inputs they were deployed with, even if you change the schema later. If you save a new version without touching the Dockerfile or requirements files, the image is not rebuilt — only the source code is updated, so iteration is fast.

Sharing and visibility

Use Manage Visibility in the top right of the tool page to share a tool with members of your organization.

GitHub integration

You can connect a tool to a GitHub repository so that every push to a chosen branch creates a new tool version. Under the Settings tab you can:
  • Link or unlink a GitHub repository and branch.
  • Toggle automatic publishing — on means new versions go live immediately; off means an admin manually publishes each version.
The GitHub integration is installed as a GitHub App. You need admin access to the GitHub account or organization in order to install it. Organization admins manage the integration at app.tamarind.bio/org-settings?tab=github-integration. A working starter repo is available at github.com/Tamarind-Bio/tamarind-custom-tools-template — fork it as a starting point.

Build and runtime

Build. Images are built in AWS CodeBuild. You may base your image on any public Docker image. Your source code is not available during the build stage — it is mounted into the container at runtime — so your Dockerfile should install dependencies only and should not COPY application code. Runtime. Your tool runs inside a Linux Docker container. CUDA 12.4 is recommended for GPU tools; reach out if you need help resolving dependency issues. The runtime container has no internet access.

Available GPUs

Set gpuType in your config.json to one of the following. Use "None" for CPU-only tools.
gpuTypeGPU
NoneCPU only
T4NVIDIA T4
L4NVIDIA L4
L40SNVIDIA L40S
A10NVIDIA A10
A100NVIDIA A100

Tool ID

Every tool has a tool ID, a fixed unique identifier used throughout the platform — including when invoking the tool through the API, batch, pipelines, or MCP. The tool ID is chosen when you create the tool from the UI and is immutable after creation.

Configuration (config.json)

A tool’s non-code settings live in a config.json file at the root of your repository. The same fields are editable in the Config tab of the UI. The schema is published at app.tamarind.bio/tamarind-tool.schema.json.

Top-level fields

FieldTypeRequiredDescription
displayNamestringyesHuman-readable tool name shown in the UI. Max 50 characters.
descriptionstringnoOne-liner shown on the tool card.
functionsstring[]noLong-description bullets — one string per bullet.
gpuTypeenumnoGPU type for the container. "None" for CPU-only. See Available GPUs.
cpuintegernoNumber of CPU cores. Between 1 and 8.
memorystringnoMemory allocation, e.g. "12Gi" or "32Gi".
envVarsobject<string,string>noEnvironment variables passed to the container at runtime. Keys must be UPPER_SNAKE_CASE.
inputsInput[]yesUser-facing input fields. Mirrors the Inputs section of the Config tab.
Do not put credentials (API keys, passwords, tokens) in envVars. Values are stored as plain environment variables, not encrypted secrets, and are visible to anyone with access to the tool’s config. The runtime container also has no internet access, so outbound credentials would not be usable anyway.

Inputs

Each entry in inputs describes one user-facing field. Every input supports these common fields:
FieldTypeRequiredDescription
namestringyesProgrammatic name. Must match ^[A-Za-z_][A-Za-z0-9_]*$. Used as the key in API calls.
typestringyesOne of the input types below.
displayNamestringnoLabel shown in the UI. Defaults to name if omitted.
descrstringnoHelp text shown under the field.
requiredbooleannoWhether the user must provide a value. Defaults to false.
The type field determines which additional fields are allowed:
Accepts a file upload. You must specify the allowed extensions.
FieldTypeRequiredDescription
extensionstring[]yesAllowed file extensions, lowercase, no dot. Example: ["csv", "tsv"].
{
  "name": "input_data",
  "type": "file",
  "displayName": "Input CSV",
  "extension": ["csv"],
  "required": true
}
A .pdb file upload. Tamarind renders a structure preview in the UI automatically.
{
  "name": "target",
  "type": "pdb",
  "displayName": "Target structure",
  "required": true
}
An .sdf file upload for small-molecule inputs.
{
  "name": "ligand",
  "type": "sdf",
  "required": true
}
A protein sequence string. Rendered as a sequence input in the UI.
FieldTypeRequiredDescription
defaultstringnoDefault sequence value.
{
  "name": "sequence",
  "type": "sequence",
  "displayName": "Target sequence",
  "required": true
}
A SMILES string for a small molecule.
FieldTypeRequiredDescription
defaultstringnoDefault SMILES value.
{
  "name": "ligand_smiles",
  "type": "smiles",
  "default": "CCO"
}
A single-line text input.
FieldTypeRequiredDescription
defaultstringnoDefault text value.
{
  "name": "job_label",
  "type": "text",
  "default": ""
}
A numeric field with optional bounds.
FieldTypeRequiredDescription
defaultnumbernoDefault numeric value.
lowerBoundnumbernoMinimum allowed value.
upperBoundnumbernoMaximum allowed value.
{
  "name": "num_samples",
  "type": "number",
  "default": 10,
  "lowerBound": 1,
  "upperBound": 100
}
A true/false toggle.
FieldTypeRequiredDescription
defaultboolean | nullnoDefault checked state. null means unset.
{
  "name": "use_relaxation",
  "type": "boolean",
  "default": false
}

Example config.json

{
  "displayName": "My Binder Scorer",
  "description": "Scores candidate binders against a target structure.",
  "functions": [
    "Scores binder–target complexes with an internal model",
    "Returns per-residue confidence"
  ],
  "gpuType": "L40S",
  "cpu": 4,
  "memory": "32Gi",
  "envVars": {
    "MODEL_VERSION": "v3",
    "LOG_LEVEL": "INFO"
  },
  "inputs": [
    {
      "name": "target",
      "type": "pdb",
      "displayName": "Target structure",
      "required": true
    },
    {
      "name": "binder_sequence",
      "type": "sequence",
      "displayName": "Binder sequence",
      "required": true
    },
    {
      "name": "num_samples",
      "type": "number",
      "default": 10,
      "lowerBound": 1,
      "upperBound": 100
    }
  ]
}

Security

  • Images are built in isolated AWS CodeBuild jobs. Source code is only mounted at runtime, never baked into the image.
  • The runtime container has no internet access, so tools cannot exfiltrate inputs or reach external services.
  • Tools are private to your organization by default. Use Manage Visibility to control who can see and run them.

Running a custom tool via the API

Custom tools are submitted through the same /submit-job endpoint as built-in tools. Pass your tool ID as type, and put your configured input values under settings:
import requests

api_key = "***************"
headers = {'x-api-key': api_key}
base_url = "https://app.tamarind.bio/api/"

params = {
  "jobName": "myJobName",
  "type": "my-tool-id",
  "settings": {
    "input_field_1": "value",
    "input_field_2": "value"
  }
}
response = requests.post(base_url + "submit-job", headers=headers, json=params)
print(response.text)
The keys inside settings must match the name fields defined in your tool’s inputs schema. See the API reference for polling job status and retrieving results.