Skip to content
FlaskTrack Laboratory Operations & Data Platform
Build with FlaskTrack · MCP · AI agents · laboratory automation

Build your first FlaskTrack MCP agent

Give an AI agent controlled access to real laboratory operations with FlaskTrack's organization-scoped MCP tool interface.

In this walkthrough, you will create a small Python agent that discovers FlaskTrack tools, searches laboratory records, executes a tool, and uses structured results to continue safely.

Dynamic tool discoveryRead the live MCP registry instead of hard-coding the API surface
Typed laboratory recordsWork with workflows, protocols, batches, samples, species, and more
Organization scopedEvery action is evaluated in the authenticated FlaskTrack organization
Permission awareAgent calls remain subject to roles, validation, and compliance controls
Structured resultsUse returned record IDs and metadata to safely continue multi-step work

What you are building

The agent will discover the tools exposed by your FlaskTrack deployment, choose a read operation, execute it through the MCP interface, and use the structured result as context for the next decision.

1
Discover toolsLoad the current FlaskTrack tool catalog from /mcp/tools.
2
Choose a toolGive the model names, descriptions, schemas, and semantic record metadata.
3
Execute through FlaskTrackSend one registered tool name and schema-valid input to /mcp/call.
4
Continue from the resultUse the concrete returned record ID instead of guessing or inventing identifiers.

Before you start

Create a FlaskTrack API credential for the integration and keep it outside your prompt, source code, browser JavaScript, and model context.

🔑
API keyUse a dedicated machine credential for the agent.
🏢
OrganizationEvery request includes the FlaskTrack organization context.
🐍
Python 3.10+The example uses Python, requests, and any model client you prefer.
🧠
LLM providerOpenAI, Anthropic, a local model, or another provider can drive the decision loop.
Step 1

Configure FlaskTrack credentials

Keep credentials in environment variables so the model never sees them.

export FLASKTRACK_URL="https://flasktrack.com"
export FLASKTRACK_ORGANIZATION="YOUR_ORGANIZATION_ID"
export FLASKTRACK_API_KEY="YOUR_API_KEY"
Step 2

Install the tiny client

python -m pip install requests
Step 3

Connect to FlaskTrack

Keep credentials in the HTTP layer rather than the prompt.

import os
import requests

BASE_URL = os.environ["FLASKTRACK_URL"].rstrip("/")

HEADERS = {
    "x-organization": os.environ["FLASKTRACK_ORGANIZATION"],
    "x-api-key": os.environ["FLASKTRACK_API_KEY"],
    "accept": "application/json",
}

def flasktrack_get(path):
    response = requests.get(
        f"{BASE_URL}{path}",
        headers=HEADERS,
        timeout=30,
    )
    response.raise_for_status()
    return response.json()

def flasktrack_post(path, payload):
    response = requests.post(
        f"{BASE_URL}{path}",
        headers={**HEADERS, "content-type": "application/json"},
        json=payload,
        timeout=60,
    )
    response.raise_for_status()
    return response.json()
Step 4

Discover the live MCP tool catalog

Do not hard-code every FlaskTrack action. Ask the running deployment what tools are currently registered.

tools = flasktrack_get("/mcp/tools")

for tool in tools:
    print(
        tool["name"],
        tool["effect"],
        tool.get("output_entity"),
    )
💡
Why discovery matters FlaskTrack's tool surface evolves with the platform. Runtime discovery lets an agent adapt to the deployed version instead of relying on a stale list copied into a prompt.
Step 5

Give the model a compact tool list

def compact_tools(tools):
    return [
        {
            "name": tool["name"],
            "description": tool["description"],
            "effect": tool["effect"],
            "input_schema": tool["input_schema"],
            "entity_fields": tool.get("entity_fields", []),
            "output_entity": tool.get("output_entity"),
        }
        for tool in tools
    ]

agent_tools = compact_tools(tools)

Keep authentication headers, API keys, cookies, and unrelated organization data outside model-visible context.

Step 6

Ask the model for one tool call

Keep the first agent intentionally simple: the model returns one registered tool name and one JSON input object.

import json

SYSTEM_PROMPT = """
You are a FlaskTrack laboratory assistant.

Choose exactly one FlaskTrack tool for the user's request.

Rules:
- Use only tool names supplied to you.
- Match the tool input schema exactly.
- Never invent FlaskTrack UUIDs.
- Treat Workflow, Protocol, Batch, Sample, Species, Tool,
  Ingredient, Plasmid, and other entity IDs as distinct types.
- Prefer read tools when you still need to identify a record.
- Return JSON only:

{
  "name": "tool_name",
  "input": {}
}
"""

def choose_tool(llm, user_request, tools):
    raw = llm(
        system=SYSTEM_PROMPT,
        user=json.dumps({
            "request": user_request,
            "tools": tools,
        }),
    )

    return json.loads(raw)

The llm function is provider-agnostic. Wrap your preferred model SDK and make it return the model's text response.

Step 7

Execute the selected FlaskTrack tool

def call_tool(tool_call):
    return flasktrack_post(
        "/mcp/call",
        {
            "name": tool_call["name"],
            "input": tool_call["input"],
        },
    )

FlaskTrack resolves the registered tool and applies its normal input validation, organization scope, permissions, route, and operation semantics.

Step 8

Put the pieces together

def run_agent_once(llm, request):
    tools = flasktrack_get("/mcp/tools")

    tool_call = choose_tool(
        llm,
        request,
        compact_tools(tools),
    )

    print("Selected tool:", tool_call["name"])
    print("Input:", json.dumps(tool_call["input"], indent=2))

    result = call_tool(tool_call)

    print("Result:")
    print(json.dumps(result, indent=2))

    return result

run_agent_once(
    llm,
    "Find the workflow used for banana multiplication.",
)

That is the core FlaskTrack agent loop: discover, decide, execute, inspect.

Step 9

Use real results for multi-step work

If one action creates a record needed by the next action, use the concrete ID returned by FlaskTrack.

workflow = call_tool({
    "name": "create_workflow",
    "input": workflow_input,
})

workflow_id = workflow["result"]["primary_id"]

batch = call_tool({
    "name": "create_batch",
    "input": {
        "name": "Agent-created batch",
        "workflow_id": workflow_id,
        "species_id": species_id,
        "planned_quantity": 24,
    },
})
🧬
Never invent future IDs Do not use strings such as workflow_id_placeholder. Execute the first operation, capture its authoritative result, and use that value in the next direct MCP call.

Optional: preview a mutation before execution

Use /mcp/prepare when your integration wants a validation or review step before direct execution.

def prepare_tool(tool_call):
    return flasktrack_post(
        "/mcp/prepare",
        {
            "name": tool_call["name"],
            "input": tool_call["input"],
        },
    )

Preparation does not execute the underlying operation. Use it for policy checks, logging, or a human confirmation surface.

Three rules that make agents dramatically safer

🔍
Search before mutationIf the agent does not know an exact record, use a FlaskTrack read tool first.
🏷️
Respect entity typesA Protocol UUID is not a Workflow UUID. Use the semantic type declared by the tool.
🛑
Stop on control failuresAuthorization, compliance, validation, and signature failures are authoritative. Do not route around them.

From demo agent to production integration

  • ✔ Use a dedicated FlaskTrack service identity and minimum required permissions
  • ✔ Keep API keys outside model-visible context
  • ✔ Discover tools from the target deployment at runtime
  • ✔ Use read tools to resolve exact records before mutation
  • ✔ Validate typed entity relationships rather than accepting arbitrary UUIDs
  • ✔ Add explicit human approval for high-impact or mutating operations
  • ✔ Use bounded retries and timeouts
  • ✔ Preserve idempotency keys where supported or required
  • ✔ Log tool names, correlation IDs, statuses, and returned record IDs without secrets
  • ✔ Treat electronic-signature and compliance controls as server-authoritative
Next steps

Build the agent around your laboratory

Start with one read workflow, add one reviewed mutation, and expand only after the integration behaves predictably against real FlaskTrack records.

Start read-only

Begin with discovery, workflow lookup, batch status, or reporting before enabling mutation tools.

Add approval

Put a human or policy gate in front of creation, updates, completion, and other operational actions.

Expand deliberately

Add tools as the agent proves reliable rather than exposing every available mutation on day one.

Your agent can now operate on the same laboratory model as your team

Full Biolab Integrated MCP Agent Example On Github

FlaskTrack gives agents a structured, permission-aware interface to laboratory records and operations without browser automation, direct database access, or a separate shadow data model.

Screenshot preview