API Documentation

MCP Server

Tamarind offers a Model Context Protocol (MCP) server that allows AI assistants and other applications to access Tamarind's computational biology tools. With the MCP server, you can:

  • Submit jobs for any tool on Tamarind, including structure prediction, protein design, docking, and more
  • Query job status and retrieve results
  • Upload and manage input files
  • Access all Tamarind tools through natural language

Example prompts you can use with your AI assistant:

  • "Submit an RFDiffusion job to design a binder for this target protein"
  • "Check the status of my recent structure prediction jobs"
  • "Get the results from my batch antibody design job"

Using Claude or ChatGPT? Open the quick connect guide for a couple-of-clicks setup with the connector prefilled. Other clients are covered under Client Setup Instructions below.

Requirements

To use Tamarind's MCP server, you need:

  1. A Tamarind account - Sign up here
  2. OAuth credentials (see OAuth 2.1 below) or an API key - Get yours at API Key or email info@tamarind.bio
  3. A compatible MCP client - Such as Claude.ai, Claude Code, VS Code, Codex, or other MCP-compatible tools
Authentication

Tamarind MCP server supports two authentication methods:

OAuth 2.1

OAuth authentication provides user-specific access control and is the recommended method for most users.

Client ID: V8apuKVjc88f0FNP

Client Secret: Sign in to view

Authorization URL: https://clerk.tamarind.bio/oauth/authorize

Token URL: https://clerk.tamarind.bio/oauth/token

Enterprise gateways and custom MCP clients often use their own OAuth callback URL. If you see a redirect_uri mismatch error, submit the exact URI your client sends and we will add it to our OAuth allowlist.

Sign in to request a redirect URI registration.

API Key

API key authentication is a simpler alternative suitable for personal use or automation. Pass your API key via the x-api-key header. View your API key at API Key or email info@tamarind.bio to request access.

Server URL: https://mcp.tamarind.bio/mcp

Client Setup Instructions

Claude.ai provides the simplest way to connect to Tamarind's MCP server with no installation required.

Prerequisites

  • Claude.ai account (free or paid)

Setup Steps

  1. Go to claude.ai
  2. Click on your profile icon → Settings
  3. Navigate to Connectors
  4. Click Add custom connector
  5. Fill in the connector details:
    • Name: Tamarind
    • Remote MCP server URL: https://mcp.tamarind.bio/mcp
  6. Expand Advanced settings and enter your Tamarind OAuth credentials (both required):
    • OAuth Client ID: V8apuKVjc88f0FNP
    • OAuth Client Secret: See the Client Secret in the Authentication section above
  7. Click Add
  8. You'll be redirected to sign in with your Tamarind account and authorize the connection

Once connected, you can use Tamarind tools directly in your Claude.ai conversations!

Uploading files? Large files are streamed to mcp.tamarind.bio with a curl command. This client's sandbox blocks outbound network by default, so the upload fails with "network unreachable" until you allow the domain: open Settings → Capabilities, enable Allow network egress, and add mcp.tamarind.bio under Additional allowed domains. Connecting the MCP server itself does not need this - only the upload curl does. (Files ≤ 4 MB can be uploaded inline without it.)

Add the Tamarind MCP server directly from the command line:

1claude mcp add --transport http tamarind https://mcp.tamarind.bio/mcp \
2  --header "x-api-key:YOUR_API_KEY_HERE"

Replace YOUR_API_KEY_HERE with your API key, then restart Claude Code.

Add Tamarind from the Connectors UI, or edit the configuration file directly.

Connectors UI (recommended)

The same four values used on Claude.ai:

  1. Open Settings → Connectors
  2. Click Add → Add custom connector
  3. Fill in the connector details:
    • Name: Tamarind
    • Remote MCP server URL: https://mcp.tamarind.bio/mcp
  4. Expand Advanced settings and enter your Tamarind OAuth credentials (both required):
    • OAuth Client ID: V8apuKVjc88f0FNP
    • OAuth Client Secret: See the Client Secret in the Authentication section above
  5. Click Add
  6. Sign in with your Tamarind account to authorize the connection

Tamarind then appears in your connectors list, and its tools are available in new conversations.

API Key Authentication (config file)

  1. Get your API key from API Key
  2. Open your Claude Desktop configuration file:
    • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
    • Windows: %APPDATA%\Claude\claude_desktop_config.json
  3. Add the Tamarind MCP server configuration:
1{
2  "mcpServers": {
3    "tamarind": {
4      "command": "npx",
5      "args": [
6        "mcp-remote",
7        "https://mcp.tamarind.bio/mcp",
8        "--header",
9        "x-api-key:YOUR_API_KEY_HERE"
10      ]
11    }
12  }
13}

Replace YOUR_API_KEY_HERE with your API key, then restart Claude Desktop.

OAuth Authentication (config file)

  1. Get your OAuth credentials from the Authentication section above
  2. Open your Claude Desktop configuration file:
    • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
    • Windows: %APPDATA%\Claude\claude_desktop_config.json
  3. Add the Tamarind MCP server configuration (see below)
  4. Restart Claude Desktop
  5. The first time you use Tamarind tools, you'll be prompted to authenticate in your browser
  6. Log in with your Tamarind account
1{
2  "mcpServers": {
3    "tamarind": {
4      "command": "npx",
5      "args": [
6        "mcp-remote",
7        "https://mcp.tamarind.bio/mcp",
8        "3334",
9        "--static-oauth-client-info",
10        "{\"client_id\":\"V8apuKVjc88f0FNP\",\"client_secret\":\"YOUR_CLIENT_SECRET\"}"
11      ]
12    }
13  }
14}

Replace YOUR_CLIENT_SECRET with your client secret (revealed in the Authentication section above), then restart Claude Desktop. Once connected, you'll see Tamarind appear in your connectors list.

Uploading files? Large files are streamed to mcp.tamarind.bio with a curl command. This client's sandbox blocks outbound network by default, so the upload fails with "network unreachable" until you allow the domain: open Settings → Capabilities, enable Allow network egress, and add mcp.tamarind.bio under Additional allowed domains. Connecting the MCP server itself does not need this - only the upload curl does. (Files ≤ 4 MB can be uploaded inline without it.)

Claude Science registers its own OAuth client, so there are no credentials to paste - leave Advanced settings empty.

Setup Steps

  1. Open Settings → Connectors
  2. Click Add connector → Remote URL
  3. Fill in the connector details:
    • Name: Tamarind
    • Remote MCP server URL: https://mcp.tamarind.bio/mcp
  4. Click Add
  5. Click Connect, then sign in with your Tamarind account to authorize the connection

Tamarind then appears under Custom in your connectors list, and its tools are available in new conversations.

Connect using API

To set it up, add your API key:

  1. Open Settings → Credentials → Add
  2. Set Name to TAMARIND_API_KEY
  3. Set Value to your API key, copied from API Key

VS Code supports MCP servers through GitHub Copilot Chat. Add the following to .vscode/mcp.json in your workspace (or use Cmd+Shift+P → "MCP: Add Server"):

1{
2  "servers": {
3    "tamarind": {
4      "type": "http",
5      "url": "https://mcp.tamarind.bio/mcp",
6      "headers": {
7        "x-api-key": "${input:tamarind-api-key}"
8      }
9    }
10  },
11  "inputs": [
12    {
13      "type": "promptString",
14      "id": "tamarind-api-key",
15      "description": "Tamarind API Key",
16      "password": true
17    }
18  ]
19}

VS Code will prompt you for the API key on first use. You can then use @tamarind in Copilot Chat to access Tamarind tools.

Refer to the VS Code MCP documentation for more details.

Tamarind Bio is published in the ChatGPT plugin directory. No configuration, API key, or installation is required.

  1. Go to chatgpt.com
  2. Open Plugins
  3. Search for Tamarind Bio
  4. Click Install
  5. Sign in with your Tamarind account when prompted to authorize the connection

Once connected, Tamarind tools are available directly in your ChatGPT conversations.

Install the published Tamarind Bio plugin in the Codex desktop app. Adding the server by hand is only needed if you installed our plugin repository as a marketplace instead.

Install the plugin (recommended)

  1. Open Plugins in the Codex sidebar
  2. Search for Tamarind Bio
  3. Click Install and complete the authorization prompt

The plugin brings the MCP server and all Tamarind skills with it. Nothing else to configure.

Add the server manually

Use this if you added Tamarind-Bio/tamarind-agent-plugin as a plugin marketplace. That install registers the server, so tamarind appears under MCPs → From plugins, but the entry carries no credentials and its Authenticate button will not connect it. Connect it with an API key instead:

  1. Get your API key from API Key
  2. Open Settings → Plugins → MCPs → Add server
  3. Set Name to tamarind
  4. Set Type to Streamable HTTP (the STDIO default is for local servers and will not connect)
  5. Set URL to https://mcp.tamarind.bio/mcp
  6. Under Headers, add x-api-key with your API key as the value
  7. Save, then start a new task so Codex discovers the tools

Codex CLI

  1. Set your API key as an environment variable in your shell profile (~/.zshrc or ~/.bashrc):
1export TAMARIND_API_KEY="your-api-key-here"
  1. Add the following to ~/.codex/config.toml:
1[mcp_servers.tamarind]
2url = "https://mcp.tamarind.bio/mcp"
3env_http_headers = { "x-api-key" = "TAMARIND_API_KEY" }
  1. Launch Codex and use Tamarind tools directly

Antigravity connects with the OAuth credentials above. Add the server to its MCP config, then authorize in the browser - there is nothing to paste back into the editor.

  1. Open Settings → Installed MCP Servers and click Open MCP Config
  2. Add the Tamarind server:
1{
2  "mcpServers": {
3    "tamarind": {
4      "serverUrl": "https://mcp.tamarind.bio/mcp",
5      "oauth": {
6        "clientId": "V8apuKVjc88f0FNP",
7        "clientSecret": "YOUR_CLIENT_SECRET"
8      }
9    }
10  }
11}
  1. Replace YOUR_CLIENT_SECRET with the Client Secret from the Authentication section above, then save the file
  2. Back in Installed MCP Servers, click Authenticate next to tamarind
  3. Sign in with your Tamarind account to authorize the connection

Until you authorize, the server shows Error: calling "initialize": Unauthorized. That is expected - it means Antigravity has the server but no token yet, and it clears once Authenticate completes.

Any MCP-compatible client (Cursor, Windsurf, or custom implementations) can connect to the Tamarind MCP server at https://mcp.tamarind.bio/mcp using API key authentication. Pass your key via the x-api-key header.

Refer to your specific client's documentation for configuration details. If you need help, email info@tamarind.bio.

Available Tools

The Tamarind MCP server exposes 33 tools. Expand any tool to see its parameters. Each tool declares whether it only reads data or may modify it, so your MCP client can flag writes before running them.

Read-only - Safe queries that never modify your data.

Estimate how LONG a job will run and how many WEIGHTED (compute) HOURS it will cost — BEFORE submitting anything. Use it to plan a run, compare tools, warn the user about a slow or expensive job, or budget a design loop. The number returned is the SAME estimate the submit path's budget gate uses, so it matches what submitting this job would quote. No job is created. Returns JSON: { jobType, numJobs, perJobRuntimeSeconds, perJobRuntimeHuman, tier, weightedHours?, note? } - perJobRuntimeSeconds / perJobRuntimeHuman: wall-clock estimate for ONE job. - numJobs: how many jobs these settings expand into (e.g. multiple sequences). - weightedHours: total compute-hour cost across the whole submission (the billing unit). Returned for PREMIUM users only; free users get `note` instead, mirroring the website (compute cost is shown to Premium users only). Args: type: Tool name — see getAvailableTools(). settings: Job settings object — see getJobSchema(jobType=type). Estimate one job at a time (a single settings object, not a list). jobName: Optional; does not create a job and does not affect the numbers. Leave as the default. atomCount: Optional atom count for MD tools (gromacs/openmm) whose runtime scales with system size; a typical default is used when omitted.

ParameterTypeRequiredDescription
typestringYes
settingsobjectYes
jobNamestringNo (default: "estimate")
atomCountintegerNo (default: null)

Get list of all available tools/job types in the Tamarind platform. Use this tool BEFORE submitting jobs to discover what tools are available. The catalog is large, so FILTER rather than listing everything: narrow by `modality` (the molecule type) and/or `function` (what the tool does), or by a free-text `search`. Call listModalities() and listTags() first to see the valid values (with descriptions) for those two filters. Args: modality: Filter by molecule type (e.g. "antibody", "protein", "peptide", "small-molecule", "nucleic-acid"). See listModalities(). function: Filter by what the tool does (e.g. "structure-prediction", "binder-design", "protein-ligand-docking"). See listTags(). category: Deprecated alias for `modality` (still honored). tag: Deprecated alias for `function` (still honored). search: Search in tool name or description custom: If true, return only custom tools; if false/None, return standard tools from database The catalog drifts as tools are added/removed, so don't guess values — every response includes the exact live `availableCategories` and `availableTags` (a.k.a. modalities and functions) computed from the current catalog, or call listModalities()/listTags() for the same vocabulary with descriptions. Returns: JSON with `tools` (name, displayName, categories, outputTypes[, tags, description]), plus `availableCategories` and `availableTags` — the live facet lists to filter against. `outputTypes` is what a tool declares it produces — molecular (pdb, sequence, sdf, smiles) and otherwise (csv, score, cif, ...). A few tools use compound values such as "pdb-list" or "sequence-list", so match by CONTAINMENT, not equality. Use it to plan a multi-step run: a molecular type here must cover what the next tool takes as input. It is OMITTED when the tool declares nothing, which means UNKNOWN — not "produces nothing", so confirm with getJobSchema rather than ruling the tool out. A type here is also not proof the tool GENERATED it: a scorer can echo its input sequence alongside the scores. getJobSchema's `taskType` is what distinguishes generating from scoring, along with the full result contract — the results CSV and its columns, i.e. the metric names to filter or rank on. Examples: - Find antibody tools: modality="antibody" - Find structure prediction tools: function="structure-prediction" - Narrow to antibody structure prediction: modality="antibody", function="structure-prediction" - Search for AlphaFold variants: search="alphafold" - Find custom tools: custom=true

ParameterTypeRequiredDescription
modalitystringNo (default: null)
functionstringNo (default: null)
categorystringNo (default: null)
tagstringNo (default: null)
searchstringNo (default: null)
custombooleanNo (default: null)

Get summary statistics about available files (counts by type). Use this BEFORE getFiles() to understand what file types are available and decide what to filter for. Returns: JSON with file counts by type and total count

No parameters.

List files available in the Tamarind workspace with filtering. IMPORTANT: This returns a LIMITED subset of files to avoid context overflow. Use 'types' and 'search' parameters to filter before listing. Use 'offset' to paginate through results. Args: types: Comma-separated file types to filter (e.g., "pdb,cif"). HIGHLY RECOMMENDED to avoid getting too many results. search: Search term to filter filenames (e.g., "output", "job123") limit: Maximum number of files to return (default 100, max 500) offset: Number of files to skip for pagination (default 0) includeMetadata: Include file size and modification time Returns: JSON with files array, count, hasMore flag, and total count Examples: - List PDB files: types="pdb" - Search for specific job: search="job_12345" - Get next page: offset=100, limit=100

ParameterTypeRequiredDescription
typesstringNo (default: null)
searchstringNo (default: null)
limitintegerNo (default: 100)
offsetintegerNo (default: 0)
includeMetadatabooleanNo (default: false)

Retrieve a list of folders in the user's storage **Query Parameters:** - **limit**: Maximum number of folders to return - **offset**: Number of folders to skip - **loadAll**: Load all folders without pagination **Responses:** - **200** (Success): List of folders - Content-Type: `application/json` - **Response Properties:** - **hasMore**: Whether there are more folders available - **total**: Total number of folders - **offset**: Current offset in the results - **limit**: Maximum number of folders returned - **Example:** ```json { "folders": [ "string" ], "hasMore": true, "total": 1 } ``` - **401**: Unauthorized - **500**: Internal server error - Content-Type: `application/json` - **Response Properties:** - **Example:** ```json { "error": "Failed to fetch folders" } ```

ParameterTypeRequiredDescription
limitintegerNoMaximum number of folders to return (default: 20)
offsetintegerNoNumber of folders to skip (default: 0)
loadAllenumNoLoad all folders without pagination - one of: true

Download a specific file from a job's S3 directory. Use listJobFiles() first to discover what files are available. This tool handles both text and binary files. Args: jobName: The name of the job (e.g., "03-11_10-proteinmpnn-9h0O-canary") fileName: The file name within the job directory (e.g., "ranked_0.pdb", "config.json") maxSizeMB: Maximum file size to download in MB (default 10, max 50) Returns: JSON with file metadata and contents (text files as string, binary as base64) Examples: - getJobFile("03-11_10-proteinmpnn-9h0O-canary", "ranked_0.pdb") - getJobFile("my-job", "config.json") Security: - Path traversal protection: fileName cannot contain '..' or start with '/' - RBAC: Only users with access to the job can download files

ParameterTypeRequiredDescription
jobNamestringYes
fileNamestringYes
maxSizeMBintegerNo (default: 10)

Fetch the output logs for a specific job from S3. This is useful for debugging job failures, checking progress, or understanding what happened during job execution. Args: jobName: The name of the job (e.g., "03-11_10-proteinmpnn-9h0O-canary") maxLines: Maximum number of lines to return from the end of the log (default 500) Returns: The output log contents (last N lines) or error message Examples: - getJobLogs("03-11_10-proteinmpnn-9h0O-canary") - getJobLogs("my-alphafold-job", maxLines=100)

ParameterTypeRequiredDescription
jobNamestringYes
maxLinesintegerNo (default: 500)

Get the detailed parameter schema for a specific job type. Checks both standard tools (DynamoDB) and custom tools (API) to find the schema. Returns the complete schema including all parameter definitions with their types, descriptions, validation rules, and metadata. Args: jobType: Job type name (e.g., "proteinmpnn", "alphafold", "rfdiffusion", or custom tool name) Returns: Complete JSON schema for the job type including: - parameters: full parameter definitions (name, type, description, required, etc.) - displayName: human-readable tool name - description: tool description - categories: tool categories - tags: tool tags - pipeline: the facts for wiring this tool as a PIPELINE NODE, and the AUTHORITATIVE source when you are building a graph. `ports` maps each input port to the `rep` it consumes; `produces`/`producesByTask` are what the node MINTS. An edge is valid when the upstream node's OUTPUT reps cover the downstream `ports[*].rep`, and the output set is wider than `produces` alone: - a `generate` node emits `produces` PLUS any `carries` reps its own input actually supplied (`produces | (carries & incoming)`). ligandmpnn produces `sequence` and carries `smiles`, so fed a ligand-bearing input it CAN feed a downstream smiles port - ruling that out on `produces` alone rejects a valid ligand-preserving pipeline. - any other node passes its input through: it emits `incoming | produces`. So compare against that computed set, not against `produces` verbatim. Do NOT chain on `outputTypes` below: that is the single-job result declaration, it can carry compound values and representations echoed from the input, and the graph validator does not consult it, so edges built from it can be rejected by validatePipeline. Also carries `taskField` (the task selector), `bindingFields` (supplied per-run in the binding, never as a node setting), `notSettable`, `paramBounds` and `outputColumns`. `wireable: false` means the tool has no pipeline rep at all - distinct from a zero-input generator, which is wireable with no ports. - outputTypes: what this tool declares it produces — molecular (pdb, sequence, sdf, smiles) and otherwise (csv, score, cif, ...); a few are compound, like "pdb-list", so match by CONTAINMENT. Chain on the molecular ones: they must cover what the next tool takes as input. Absent means the tool declares nothing (UNKNOWN), not that it produces nothing. - outputs: what the results look like — `taskType` (`generate` vs `score` is what tells you whether the tool creates a molecule or just annotates one), `produces` (molecular representations the output carries — as a column of the result table OR as a file written alongside it — INCLUDING any echoed from the input, so a scorer can list one), `mainCSV`, and `columns` (name, type, description, units). The column names are the metrics to filter or rank results on. Absent when the tool declares no contract. For a tool whose output depends on the task it runs, `outputs.byTask` gives the per-task contract and is AUTHORITATIVE for the task you set — read it instead of the scalar fields, which summarize across tasks and are NOT guaranteed to describe the task selector's default. Each entry is complete (a task inherits the top-level `mainCSV`/`taskType` when it doesn't redeclare them) and carries `generates`: what that task creates fresh, which is empty for a scoring task whose table still echoes its input. A column carrying a `tasks` list appears only in those tasks; an UNTAGGED column is simply not task-gated in the declaration, which does not promise every task fills it — read `generates` for what a task actually produces. `outputs.byTaskNote` restates this on the response. - custom: true if this is a custom tool (only present for custom tools) Examples: - getJobSchema("alphafold") - getJobSchema("proteinmpnn") - getJobSchema("protein_properties_wmo6g") # custom tool

ParameterTypeRequiredDescription
jobTypestringYes

List your jobs (status, timing, score), or fetch one job by name. By default each job's bulky input JSON is OMITTED: for a big batch it dominates the payload and overflows the context. The input blob is named `Sequence` on the production API and `Settings` on the legacy API, so both are dropped (whichever a job carries). Set includeSequences=true to keep them, or use getResult()/getJobFile() for a single job's full inputs/outputs. The `Score` field is retained as a ranking signal. Args: jobName: Return just this one job. batch: Return all jobs in this batch. startKey: Pagination cursor from a previous call (to page past `limit`). limit: Max jobs to return (default 1000); a startKey is returned if more remain. organization: If true, return all jobs across your organization. includeSubjobs: If true, include batch subjobs (only top-level jobs by default). jobEmail: Return jobs for another member of your organization. includeSequences: If true, keep each job's bulky input JSON (the `Sequence`/`Settings` fields, omitted by default). Returns: JSON: {jobs: [...], startKey?, statuses?} for listings, or a single job object when jobName is given.

ParameterTypeRequiredDescription
jobNamestringNo (default: null)
batchstringNo (default: null)
startKeystringNo (default: null)
limitintegerNo (default: 1000)
organizationbooleanNo (default: null)
includeSubjobsbooleanNo (default: null)
jobEmailstringNo (default: null)
includeSequencesbooleanNo (default: false)

Read an existing structure import. Only status=ingested is ready for Jobs. Use moleculeIds as pickerComplex complexId values together with groupId; omit settings.pdbFile. Poll the same importId while queued. Report failed or cancelled imports instead of submitting their inputs.

ParameterTypeRequiredDescription
importIdstringYes

Get one pipeline run's status and per-step progress. Poll this to follow a run submitted with submitPipeline. Each step reports its own status, how many of its jobs are complete, and `outputGroup` — the molecules group that step produced. Use getPipelineRunResults(runId) to read those outputs. Args: runId: The run id (the `id` returned by submitPipeline). Returns: JSON PublicRun: { id, templateId, templateVersion, name, source, status, startedAt, completedAt, inputs, steps: [{id, node, label, type, status, startedAt, completedAt, outputCount, jobsTotal, jobsComplete, outputGroup}] }.

ParameterTypeRequiredDescription
runIdstringYes

Read a pipeline run's OUTPUTS — the molecules each step produced, with their scores — rather than just its status. Each step of a run writes its results into a molecules group; this resolves those groups and returns the molecules inline. A molecule's `metadata` holds its per-tool scores, which is what you rank and compare on. Results appear per step as it finishes, so this is useful before the whole run is complete. A step with no `outputGroup` yet has produced nothing so far. To page through a step that produced more than `limit` molecules: call again with `node` set to that step and `cursor` set to its `nextCursor`. Args: runId: The run id. node: Only this step's outputs (the `node` value from getPipelineRun). Omit for every step that has produced output. limit: Molecules per step, 1-100 (default 25). Keep it small — molecules carry their sequences, files and full score history. cursor: A `nextCursor` from a previous call, to fetch the next page. Requires `node`, since a cursor belongs to one step's group. Returns: JSON: { runId, status, steps: [{node, label, status, outputGroup, molecules[], nextCursor, error?}] }.

ParameterTypeRequiredDescription
runIdstringYes
nodestringNo (default: null)
limitintegerNo (default: 25)
cursorstringNo (default: null)

Get the pipeline IR schema and the full authoring guide. Call this BEFORE writing a `pipeline` graph for submitPipeline/validatePipeline — the IR has rules that are not guessable, and authoring blind reliably produces graphs the API rejects. Returns the complete, uncompressed JSON Schema plus the prose guide. Both are fetched live from the app host, so they always match the deployed validator. The guide covers, among other things: the four node kinds; that there is NO edge list (topology lives inside each node's `inputs`); that every molecule `user_input` needs a reference group; that a `filter` node must either set `intersect: true` or carry at least one rule; and that residue selections are supplied per-run in the BINDING (`residuesByChain`), never as a node setting. Returns: JSON: { schemaUrl, guideUrl, schema, guide }. On failure, `error` plus the two URLs so you can read them another way.

No parameters.

Get one pipeline template: its graph, its bindable inputs, and its versions. The `inputs` array is the important part — it is the manifest of what you must pass as `bindings` to submitPipeline/validatePipeline. Each entry gives the input node id to key the binding by, whether it takes molecules or a file, the molecule type it expects, its reference chain labels, and any `residueFields`. A non-empty `residueFields` means that run REQUIRES a `residuesByChain` selection in this input's binding. Args: templateId: The template id (from listPipelineTemplates). version: A version handle like "v2". Omit for the version a default run would use (published, else latest). Returns: JSON PublicTemplate: { id, name, description, isPublished, publishedVersion, version, pipeline (the IR), inputs[], versions[], createdAt, updatedAt }.

ParameterTypeRequiredDescription
templateIdstringYes
versionstringNo (default: null)

Retrieve results for a completed job **Responses:** - **200** (Success): Job results - Content-Type: `application/json` - **Example:** ```json "https://s3.amazonaws.com/bucket/job-results.zip" ``` - **400**: Bad request - **401**: Unauthorized - **404**: Job not found

ParameterTypeRequiredDescription
jobNamestringYesName of the job to get results for
jobEmailstringNoEmail of another member of your team (optional)
fileNamestringNoPath to a specific file in the job results (optional)
pdbsOnlybooleanNoReturn only PDB files (optional)

List all files in a job's S3 directory. This shows what output files were generated by the job, allowing you to discover available files before downloading them with getJobFile(). IMPORTANT: The returned s3Path can be used directly in submitJob to chain jobs! No need to download and re-upload - just pass the s3Path to the next job's file parameters. Args: jobName: The name of the job (e.g., "03-11_10-proteinmpnn-9h0O-canary") Returns: JSON with list of files, their sizes, and S3 paths for job chaining Examples: - Chain jobs: listJobFiles("job1") then use files[0].s3Path in submitJob("job2", {"pdb": s3Path}) - Download files: getJobFile(jobName, fileName)

ParameterTypeRequiredDescription
jobNamestringYes

List the biological MODALITIES (molecule types) you can filter tools by. A modality is what KIND of molecule a tool works on — protein, antibody, peptide, small-molecule, nucleic-acid, etc. This is one of the two axes for narrowing the tool catalog (the other is function — see listTags()). Call this BEFORE getAvailableTools to pick a modality, then filter with getAvailableTools(modality="<value>"). Combine with function= to narrow more. Returns: JSON: { modalities: [{ value, label, description?, toolCount }], ... }. Pass `value` (e.g. "antibody") as the modality filter. `toolCount` is how many tools you can currently see in that modality.

No parameters.

List molecule groups — the way to FIND an existing group to feed a pipeline. A group id from here is the same id a binding takes ({"group": "<id>"}) and the same id a run reports as a step's `outputGroup`, so a group produced by one run can be fed straight into the next. Rows are summaries: id, name, type, moleculeCount, source, tags, timestamps — no molecules. Use searchMolecules(group=<id>) to see what is inside one. Args: search: Case-insensitive LITERAL substring of the group name — `_` and `%` match themselves, not as wildcards. scope: "mine" (default — groups you created) or "org" (your whole organization's). sort: "recent" (default), "name", or "size". dir: "asc" or "desc". limit: Page size, 1-200 (default 50). Keep it small and follow `nextCursor` — a full 200-row page costs roughly 19k tokens of context. cursor: `nextCursor` from a previous page. Returns: JSON: { items: [{id, name, type, moleculeCount, source, tags, status, createdAt, updatedAt}], nextCursor }.

ParameterTypeRequiredDescription
searchstringNo (default: null)
scopestringNo (default: null)
sortstringNo (default: null)
dirstringNo (default: null)
limitintegerNo (default: null)
cursorstringNo (default: null)

List pipeline runs. Note that pipeline runs do NOT appear in getJobs() — that endpoint covers single jobs and batches only, so this is the way to see them. Args: status: Filter by run status. templateId: Only runs of this template. owner: "mine" (default) or "org". limit: Page size, 1-200 (default 50). cursor: `nextCursor` from a previous page. Returns: JSON: { items: [{id, templateId, templateVersion, name, source, status, startedAt, completedAt}], nextCursor }.

ParameterTypeRequiredDescription
statusstringNo (default: null)
templateIdstringNo (default: null)
ownerstringNo (default: null)
limitintegerNo (default: 50)
cursorstringNo (default: null)

List saved pipeline templates you can run. Use this to find an existing pipeline instead of authoring a graph from scratch — then read it with getPipelineTemplate() and run it with submitPipeline(templateId=...). Rows are summaries only (no graph). Call getPipelineTemplate for the IR and the list of inputs you must bind. Args: search: Match templates by name. owner: "mine" (default) or "org" for your whole organization's templates. isPublished: True for published templates only, False for unpublished. limit: Page size, 1-200 (default 50). cursor: `nextCursor` from a previous page. Returns: JSON: { items: [{id, name, description, isPublished, versionCount, runCount, createdBy, createdAt, updatedAt}], nextCursor }.

ParameterTypeRequiredDescription
searchstringNo (default: null)
ownerstringNo (default: null)
isPublishedbooleanNo (default: null)
limitintegerNo (default: 50)
cursorstringNo (default: null)

List the FUNCTIONS (a.k.a. tags) you can filter tools by. A function is what a tool DOES — structure-prediction, binder-design, protein-ligand-docking, binding-affinity, etc. This is one of the two axes for narrowing the tool catalog (the other is modality — see listModalities()). Call this BEFORE getAvailableTools to pick a function, then filter with getAvailableTools(function="<value>"). Combine with modality= to narrow more. Returns: JSON: { functions: [{ value, label, description?, toolCount }], ... }. Pass `value` (e.g. "structure-prediction") as the function filter. `toolCount` is how many tools you can currently see with that function.

No parameters.

Validate a job's settings BEFORE submitting, so you can catch missing or invalid fields and fix them (or ask the user) instead of submitting and surfacing a 400. Returns JSON: { valid, normalized?, error?, ... }. HTTP 200 is used for both outcomes — read the `valid` field. When valid is false, `error` explains the first problem. IMPORTANT: only submit settings that came back valid:true WITHOUT a `mutatedFields` warning. A mutated input means the validator silently dropped disallowed characters (e.g. non-standard residues), so submitting as-is would run the job on ALTERED input — fix the input and re-validate first. Args: jobName: Unique name for the job (letters, digits, '_' and '-'). type: Tool name — see getAvailableTools(). settings: Job settings object — see getJobSchema(jobType=type).

ParameterTypeRequiredDescription
jobNamestringYes
typestringYes
settingsobjectYes

Validate a pipeline run WITHOUT executing or saving anything. ALWAYS call this before submitPipeline — a pipeline run costs real compute, and most authoring mistakes are caught here. Takes the arguments submitPipeline validates: the graph (`pipeline` or `templateId`+`version`), `bindings`, `settings` and `name`. submitPipeline's remaining arguments — `runName`, `project`, `idempotencyKey` — name and tag the run rather than describing it, and the validator does not read them, so passing them here would change nothing. If `validationUnavailable` comes back, the validator could not be reached and your pipeline was NOT judged — retry rather than editing the graph. Returns { valid, errors[] } at HTTP 200 for both outcomes; read `valid`. Each error carries a stable `code`, the `node` it belongs to, the `field` at fault where there is one, and a `message` telling you how to fix it. Codes you will actually hit include `required-field-unset` (often a residue selection that belongs in the binding, not in settings), `input-unbound`, `tool-unknown`, `setting-invalid`, `param-out-of-range`, `chain-incompatible` and `molecule-class-incompatible` (the upstream tool does not produce what the downstream tool consumes). Fix the reported errors and re-validate until `valid` is true. Args: name: A name for the pipeline/run. Required. bindings: One entry per input node, keyed by the input node's id. Give molecules exactly one way: {"group": "<groupId>"} for an existing molecule group, or raw values — {"sequences": [...]}, {"smiles": [...]}, {"pdbs": ["<uploadedPath>"]}, {"sdfs": [...]} — and the server creates the group for you. A file input takes {"file": "<path>"}. Add "residuesByChain": {"A": "1-76"} where the template's residueFields ask for one, and "chainMapping" only when your chain IDs differ from the template's reference chains. pipeline: INLINE mode — a full pipeline IR. Call getPipelineSchema() first. Mutually exclusive with templateId. templateId: REFERENCE mode — an existing template to run. version: REFERENCE mode only — a version handle like "v2". settings: REFERENCE mode only — per-node overrides {nodeId: {settingKey: value}}, limited to the settings the template's author marked editable. Returns: JSON: { valid: bool, errors: [{code, severity, node, field, message}] }.

ParameterTypeRequiredDescription
namestringYes
bindingsobjectYes
pipelineobjectNo (default: null)
templateIdstringNo (default: null)
versionstringNo (default: null)
settingsobjectNo (default: null)
Write - Create new jobs, batches, or files.

Prepare an uploaded PDB for Protein Solubilization through the existing Molecules API. fileName is the bare filename returned by uploadFile; nested storage paths are not accepted. Returns groupId and importId after commit, without waiting for ingestion or submitting compute. Call getMoleculeImport until status=ingested. Reuse the resulting reference across batch rows that use the same structure.

ParameterTypeRequiredDescription
fileNamestringYes
namestringYes

Submit MANY jobs as a SINGLE batch (one tool call, not one per job). Submits the settings you construct to the Tamarind API (POST https://app.tamarind.bio/api/submit-batch). Requests only ever go to the Tamarind platform; the endpoint is fixed and not caller-controlled. ALWAYS prefer this over calling submitJob in a loop. If you are about to submit more than one job of the same `type`, use submitBatch instead — it is one request, groups the jobs under a shared batch name, and is far cheaper than N separate submissions. Two ways to provide the per-job inputs: 1. settings + jobNames (explicit, full control): Provide an array of per-job settings objects and a matching array of job names. Use this when each job needs different parameters. 2. fromFile (a fasta or csv of sequences): Pass a workspace filename or a job s3Path (from listJobFiles) to a .fasta/.fa or .csv containing one sequence per record. Each sequence becomes one job. Example: submitBatch(batchName="fold-designs", type="alphafold", fromFile="designs.fasta") CHAINING FROM A FINISHED JOB (e.g. ProteinMPNN -> AlphaFold/Chai). There is no mode that reads a job's output for you. Read the job's results yourself (listJobFiles, then getJobFile on the sequence/metrics file), choose the sequences to fold, and pass them as `settings` (one object per sequence) or point `fromFile` at the job's own fasta/csv s3Path, or at a fasta/csv you uploaded. Nothing about the downstream job's configuration comes from the source job, so set it here (sharedSettings or per-row settings). NAMING RULE. Never reuse `batchName` for one of the `jobNames`. The batch is itself a job under that name, so a job sharing it collides and the batch never finishes; the server rejects the submission with 400. The comparison is against the CLEANED name — characters outside [A-Za-z0-9._-] are stripped and whitespace becomes "_", so a batch sent as "my batch" is stored as "my_batch", and that is the value no job may equal. Args: batchName: Unique name for the batch (also the prefix for auto job names). type: Tool to run for EVERY job in the batch (e.g. "alphafold", "chai"). All jobs in a batch use the same tool. Use getJobSchema(type) for params. settings: (mode 1) Array of per-job settings objects. jobNames: (mode 1) Array of job names, same length as settings, and none equal to batchName (see NAMING RULE). For fromFile you can omit this — names are auto-generated as "{batchName}-1", "{batchName}-2", ... fromFile: (mode 2) Workspace path / job s3Path to a fasta or csv of sequences. sequenceField: Settings field each sequence is placed into (default "sequence" — correct for alphafold/chai/boltz/esmfold). topN: (fromFile) Keep only the top-N sequences (ranked by an MPNN/overall_confidence score in the file when available). Omit, or pass <=0, to fold ALL (default). sharedSettings: Settings applied to every job in any mode, e.g. {"useMSA": true, "numModels": "5"}. In mode 1 a key set in a job's own settings object wins over it. maxRuntimeSeconds: Optional per-job timeout (integer seconds, >=1). weightedHoursBudget: Optional budget for the whole batch (number, >=0). Note: sequences are NOT de-duplicated — each record in the source becomes its own job (folding an identical sequence twice is left to the caller). Returns: JSON summary: batchName, type, count, jobNames, source, and the backend response. On problems, an "error" field with a hint.

ParameterTypeRequiredDescription
batchNamestringYes
typestringYes
settingsarrayNo (default: null)
jobNamesarrayNo (default: null)
fromFilestringNo (default: null)
sequenceFieldstringNo (default: "sequence")
topNintegerNo (default: null)
sharedSettingsobjectNo (default: null)
maxRuntimeSecondsintegerNo (default: null)
weightedHoursBudgetnumberNo (default: null)

Submit ONE job for protein analysis using one of the available tools. Submits the settings you construct to the Tamarind API (POST https://app.tamarind.bio/api/submit-job). Requests only ever go to the Tamarind platform; the endpoint is fixed and not caller-controlled. IMPORTANT: Use this only for a single job. If you are about to submit more than one job (e.g. folding several sequences, scanning variants, or chaining a design job's output into structure prediction), call submitBatch ONCE instead of calling submitJob in a loop. Looping submitJob is slower, ungroupable, and rate-limited. For ProteinMPNN -> AlphaFold/Chai, read the design job's results (listJobFiles / getJobFile), choose the sequences, and pass them to submitBatch as `settings` or as a fasta/csv via `fromFile`. **Responses:** - **200** (Success): Job submitted successfully - **400**: Bad request - invalid parameters - **401**: Unauthorized - invalid API key - **429**: Too many requests - rate limit exceeded

ParameterTypeRequiredDescription
jobNamestringYesUnique name for the job
typestringYesTool to use for the job. IMPORTANT: Use getAvailableTools() to discover available tools dynamically. Tool list is updated frequently - do not rely on hardcoded values. Example workflow: 1. Call getAvailableTools(search="alphafold") to find tools 2. Call getJobSchema(jobType="alphafold") to get parameter schema 3. Call submitJob with the tool name and settings
settingsobjectYesJob-specific settings. Structure depends on job type. IMPORTANT: Use the getJobSchema tool to get the exact parameter schema for your job type. Example: getJobSchema(jobType="proteinmpnn") returns the detailed schema with all parameters. Documentation: https://app.tamarind.bio/api-docs

Run a multi-tool pipeline. This SPENDS COMPUTE — call validatePipeline on the same graph and bindings first, and only submit once it reports valid:true. Two modes. Pass `pipeline` (a full IR graph) to author and run in one call: the server saves a template you own, unpublished, and runs it. Or pass `templateId` to run a template you already have, which is how you re-run without re-authoring the graph. Poll the returned run with getPipelineRun(runId), and read its outputs with getPipelineRunResults(runId). Args: name: Name for the pipeline. In inline mode it names the created template too. Required. bindings: One entry per input node, keyed by the input node's id. Give molecules exactly one way: {"group": "<groupId>"}, or raw values — {"sequences": [...]}, {"smiles": [...]}, {"pdbs": [...]}, {"sdfs": [...]} — and the server creates the group for you (and rolls it back if the submit fails). A file input takes {"file": "<path>"}. Add "residuesByChain": {"A": "1-76"} where a tool needs a residue selection, and "chainMapping" only when your chain IDs differ from the template's reference chains. pipeline: INLINE mode — a full pipeline IR. Call getPipelineSchema() first. Mutually exclusive with templateId. templateId: REFERENCE mode — an existing template to run. version: REFERENCE mode only — a version handle like "v2". Omit for published-then-latest. settings: REFERENCE mode only — per-node overrides {nodeId: {settingKey: value}}, limited to the template's editable settings. runName: An explicit name for THIS run. Omit to derive a unique one from `name`. project: Organization project id to stamp on the jobs this run creates. idempotencyKey: A client key that makes a retried submit safe. Returns: JSON PublicRun: { id, templateId, templateVersion, name, status, startedAt, completedAt, inputs, steps[] }. `id` is the runId.

ParameterTypeRequiredDescription
namestringYes
bindingsobjectYes
pipelineobjectNo (default: null)
templateIdstringNo (default: null)
versionstringNo (default: null)
settingsobjectNo (default: null)
runNamestringNo (default: null)
projectstringNo (default: null)
idempotencyKeystringNo (default: null)
Destructive - Delete, cancel, or overwrite existing data.

Soft-stop every member of a batch or pipeline in one call. Flips JobStatus to "Stopped" on all batch children, all pipeline stages, and the pipeline parent row (if one exists). Doesn't delete rows or outputs. Use this when the user wants to stop a batch or pipeline as a unit. Don't call cancelJob N times — this fans out server-side via the Batch-index GSI in one round-trip. Pipeline support is for LEGACY pipelines only: their stages carry `Batch=<pipeline_name>`, so a pipeline name resolves through the same index a batch name does, and the parent row (Type='pipelines') is looked up separately via JobName-index and cancelled too. This does NOT stop a run created by submitPipeline. Those are Type='pipeline-run', a deliberately different job type that neither index reaches — use stopPipelineRun(runId) for them. Only cancels jobs in "In Queue", "Running", or "Pending" status — already-terminal jobs are skipped. **Responses:** - **200** (Success): Batch cancellation summary - Content-Type: `application/json` - **Response Properties:** - **cancelledCount**: Number of rows successfully flipped to Stopped. - **totalChildren**: Total batch/pipeline children found (any status). - **cancellableChildren**: Children in In Queue / Running / Pending — the subset eligible for cancel. - **parentCancelled**: True if a pipeline parent row was found and cancelled too. - **Example:** ```json { "success": true, "message": "Cancelled 12 jobs in batch \"my-batch\"", "batchName": "string" } ``` - **400**: batchName missing or invalid - **401**: Unauthorized - **404**: No batch or pipeline with that name found for this user

ParameterTypeRequiredDescription
batchNamestringYesBatch or pipeline name — same value used as `batchName` for submit-batch or `pipeline_name` for buildPipeline.

Soft-stop a job — flips JobStatus to "Stopped" (same value the website cancel button writes) without deleting the row or outputs. Only works on jobs in "In Queue", "Running", or "Pending" status; jobs already in a terminal state are rejected with 400. Accepts either `jobName` (preferred — looked up via JobName-index GSI server-side, no need for callers to know the Id) or `jobId` (the DDB Id, if already known). At least one is required. Different from deleteJob, which permanently removes the row + outputs. cancelJob preserves both. **Responses:** - **200** (Success): Job cancelled - Content-Type: `application/json` - **Response Properties:** - **Example:** ```json { "success": true, "message": "Job cancelled successfully", "jobId": "string" } ``` - **400**: Job not in a cancellable state (already Completed / Failed / Cancelled) - **401**: Unauthorized - **403**: Access denied (job belongs to another user) - **404**: Job not found

ParameterTypeRequiredDescription
jobNamestringNoName of the job to cancel (preferred input).
jobIdstringNoDDB row Id (alternative input, use if already known).

Delete a workspace file (or a whole folder) from your Tamarind account. Pass `filePath` to delete one file, or `folder` to delete every file under a folder. After a successful delete the cached file listing is invalidated, so the file stops appearing in getFiles/getFileStats immediately. Args: filePath: Name/path of the file to delete (e.g. "path/to/myFile.txt"). folder: Folder path — deletes all files under it. Returns: JSON confirmation, e.g. {"message": "File deleted successfully"}.

ParameterTypeRequiredDescription
filePathstringNo (default: null)
folderstringNo (default: null)

Permanently delete a job by name (if it's a batch, all of its subjobs are deleted too). Unlike cancelJob (which only stops a running job without removing it), this permanently removes the job row and its outputs. Returns a plain-text confirmation; raises a clean error if the job doesn't exist or isn't yours. Args: jobName: Name of the job (or batch) to delete. Returns: Plain-text confirmation, e.g. "myJob deleted".

ParameterTypeRequiredDescription
jobNamestringYes

Stop a running pipeline. Steps that already finished keep their outputs; work still in flight is cancelled. Use this rather than cancelBatch — cancelBatch only understands the older pipeline job layout and will not stop one of these runs. Args: runId: The run id to stop. Returns: JSON PublicRun, reflecting the run's state after the stop.

ParameterTypeRequiredDescription
runIdstringYes

Upload a local file to your Tamarind file store (up to 200 MB). RECOMMENDED (up to 200 MB) — call with just `filename` to get a short-lived `uploadUrl` on mcp.tamarind.bio, then stream your file to it with ONE shell command: curl -T /path/to/<filename> "<uploadUrl>" The bytes go straight from your machine to your store. Afterwards the file appears in getFiles(), the web Database tab, and can be referenced by its BARE `filename` in submitJob/submitBatch file fields. IF curl FAILS with "could not resolve host" / "network unreachable" / HTTP 000, your MCP client's sandbox is blocking outbound network. The MCP tools keep working (they go through the client, not your shell) — only the upload curl needs egress to the uploadUrl's host (mcp.tamarind.bio in production). The exact host to allow is named in the uploadUrl/instructions you get back; tell the USER to allow that host, then re-run the curl: - Claude Desktop / Claude.ai: Settings -> Capabilities -> "Allow network egress" -> under "Additional allowed domains" add that host - Other MCP clients (e.g. Codex): add that host to the client's network / egress allowlist. FALLBACK (no shell available, or a small file you can't enable egress for): pass the file's bytes inline via `content` (text, or base64 for binary), up to the 4 MB inline cap. The server writes them directly — no curl, no egress: uploadFile(filename="target.pdb", content=<file text>) uploadFile(filename="model.bcif", content=<base64>, encoding="base64") FLAT FILES ONLY: pass a bare name like "target.pdb" (no "/"). Args: filename: Bare destination name, e.g. "target.pdb". content: FALLBACK only — the file's bytes inline (no curl, up to 4 MB). Leave EMPTY to get an uploadUrl to stream to (recommended). encoding: "text" (default; for text like PDB/CIF/FASTA) or "base64" (for binary or non-UTF8 inline content). contentType: MIME type to store (optional). Returns: JSON. Default: {"uploadUrl", "filename", "instructions", ...}. Inline fallback: {"success": true, "filename", "bytes", "hint"}.

ParameterTypeRequiredDescription
filenamestringYes
contentstringNo (default: "")
encodingstringNo (default: "text")
contentTypestringNo (default: "application/octet-stream")

Each tool corresponds to the REST API endpoints documented in our API reference. Use the sidebar to explore each endpoint in detail.

Troubleshooting

Connector not appearing

  • Save configuration file in the correct location and restart your client
  • Verify JSON syntax is correct (no missing commas or brackets)

Authentication failing

  • Verify your credentials (OAuth client ID/secret or API key) are correct and active
  • For API key: ensure the key is passed via the x-api-key header
  • For OAuth: log in with your Tamarind account and try again if a timeout occurs

Changes not applying

  • Save the file, then completely quit and restart your client (not just refresh)

Tools not working

  • Verify your authentication is still valid and try reconnecting

Sandboxed clients (Claude.ai, Claude Desktop) block outbound network by default. The MCP connection still works - it rides the client's connector - but the assistant's own requests to mcp.tamarind.bio are blocked, which shows up as a 403 or a file upload that fails with "network unreachable" / HTTP 000. It is not an authentication failure (those return 401). Allow the domain's egress:

  • Claude Desktop / Claude.ai: Settings → Capabilities → enable Allow network egress → under Additional allowed domains add mcp.tamarind.bio, then retry.
  • Small files (≤ 4 MB) can be uploaded inline by the assistant without enabling egress.

Need help? Contact us at info@tamarind.bio