Goal: Expose a real API endpoint to the model — the model calls your tool, TengineAI forwards the request to your server with bearer token authentication, and your server returns a result.
By the end you will see the exact headers TengineAI injects, the authenticated request your server receives, and the model's natural language response.
For the meaning of every tool field, see the Custom Tools Reference.
A CRM contact lookup tool. The model calls crm-contacts-get_by_id, TengineAI POSTs to your server with a static bearer token, and your server returns the contact record.
A minimal FastAPI endpoint that validates the bearer token and responds to TengineAI tool calls:
# server.py
import os
from fastapi import FastAPI, Header, HTTPException, Request
import uvicorn
app = FastAPI()
BEARER_TOKEN = os.environ["TENGINEAI_API_KEY"]
CONTACTS = {
"c_001": {"id": "c_001", "name": "Alice Johnson", "email": "alice@acme.com", "tier": "enterprise"},
"c_002": {"id": "c_002", "name": "Bob Smith", "email": "bob@startup.io", "tier": "pro"},
}
@app.post("/v1/contacts/lookup")
async def lookup_contact(
request: Request,
authorization: str = Header(...),
x_tengine_project_id: str = Header(default=""),
x_tengine_member_id: str = Header(default=""),
x_tengine_request_id: str = Header(default=""),
):
# Validate bearer token
if authorization != f"Bearer {BEARER_TOKEN}":
raise HTTPException(status_code=401, detail="Unauthorized")
body = await request.json()
print(f"Request ID: {x_tengine_request_id}")
print(f"Project ID: {x_tengine_project_id}")
print(f"Member ID: {x_tengine_member_id}")
print(f"Body: {body}")
contact_id = body.get("contact_id")
contact = CONTACTS.get(contact_id)
if not contact:
return {"found": False, "contact_id": contact_id}
return {"found": True, "contact": contact, "requested_by_member": x_tengine_member_id}
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Run it:
pip install fastapi uvicorn
python server.py
TengineAI cannot reach localhost directly — expose it with a tunnel:
ngrok http 8000
# Copy the https:// URL — you'll use it as the Base URL in Step 2
In your TengineAI Dashboard, open your project and go to Tools → Create Tool.
Basic Information
crm-contacts-get_by_idLook up a CRM contact by their ID. Returns name, email, and account tier.Request
https:// URL (e.g. https://abc123.ngrok.io)POST/v1/contacts/lookupAuthentication Strategy
tengine_... API keyTengineAI will add Authorization: Bearer tengine_... to every outbound request. Your server validates it against the same key.
Advanced — Input Schema
{
"type": "object",
"properties": {
"contact_id": {
"type": "string",
"description": "The contact ID to look up (e.g. c_001)"
}
},
"required": ["contact_id"]
}
Click Create Tool, then confirm it appears as enabled in your project's tool list.
import json
import os
import anthropic
client = anthropic.Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])
response = client.beta.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Look up contact c_001 and tell me their account tier.",
}
],
mcp_servers=[
{
"type": "url",
"url": "https://app.tengine.ai/mcp/",
"name": "tengineai",
"authorization_token": os.environ["TENGINEAI_API_KEY"],
}
],
betas=["mcp-client-2025-04-04"],
)
for block in response.content:
if block.type == "text":
print(block.text)
elif block.type == "mcp_tool_use":
print(
f"[tool_use] server={block.server_name} tool={block.name} id={block.id} "
f"input={json.dumps(block.input, default=str)}"
)
elif block.type == "mcp_tool_result":
print(
f"[tool_result] id={block.tool_use_id} is_error={getattr(block, 'is_error', False)}"
)
print(block.content)
export ANTHROPIC_API_KEY="sk-ant-..."
export TENGINEAI_API_KEY="tengine_..."
pip install anthropic
python run.py
Expected output:
Alice Johnson is on the Enterprise tier. Her email is alice@acme.com.
Your server logs will show exactly what TengineAI forwarded:
Request ID: 7f3a9b12-e4c8-4d1a-b290-1f8e3c72aa40
Project ID: 42
Member ID: (empty — plain API key used)
Body: {"contact_id": "c_001"}
The Authorization: Bearer my-secret-token-changeme header will also be present — your server checked it before reaching the log statements.
When a member session token (tng_mst_...) is used instead of an API key, Member ID will contain the user's external ID.
Test your endpoint directly to confirm the bearer token check works before connecting TengineAI:
curl -X POST "http://localhost:8000/v1/contacts/lookup" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $TENGINEAI_API_KEY" \
-H "X-Tengine-Project-Id: 42" \
-H "X-Tengine-Member-Id: " \
-H "X-Tengine-Request-Id: test-local" \
-d '{"contact_id":"c_001"}'
If your server returns the contact record, the auth check is correct and TengineAI's live requests will pass.
require_member: true, use a member session token (tng_mst_...), not a plain API keybase_url is publicly reachable — TengineAI cannot reach localhost directlybase_url matches the current tunnel addressTENGINEAI_API_KEYcurl simulation in Step 5 to test locally before involving TengineAIAdd a logging middleware to print every inbound header:
from fastapi import Request
import logging
@app.middleware("http")
async def log_requests(request: Request, call_next):
logging.info(f"Headers: {dict(request.headers)}")
return await call_next(request)
For the full list of headers TengineAI injects and when each is present, see Identity Headers in the Custom Tools Reference.