Start read-only
Begin with discovery, workflow lookup, batch status, or reporting before enabling mutation tools.
Build with FlaskTrack ยท MCP ยท AI agents ยท laboratory automation
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
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.
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
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
python -m pip install requests
Step 3
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
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
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
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
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
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
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.
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.
๐
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.
Next steps
Start with one read workflow, add one reviewed mutation, and expand only after the integration behaves predictably against real FlaskTrack records.
Begin with discovery, workflow lookup, batch status, or reporting before enabling mutation tools.
Put a human or policy gate in front of creation, updates, completion, and other operational actions.
Add tools as the agent proves reliable rather than exposing every available mutation on day one.
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.