Wrap Loop's REST endpoints as FunctionTool objects and pass them to FunctionAgent — or use AgentWorkflow.from_tools_or_functions with plain Python functions. The agent searches, verifies, and reports outcomes automatically. No key required in the free tier.
FunctionAgent uses native function calling — the right choice for OpenAI and Anthropic models. Wrap each Loop REST call with FunctionTool.from_defaults(fn=); LlamaIndex builds the JSON schema from type hints and docstrings. Use ReActAgentfrom the same module for LLMs that don't support function calling.
pip install llama-index llama-index-llms-openai requestsimport asyncio import requests from llama_index.core.tools import FunctionTool from llama_index.core.agent.workflow import FunctionAgent from llama_index.llms.openai import OpenAI BASE = "https://api.stayinloop.dev/v1" def search_loop(query: str, location: str = "Kreuzberg, Berlin") -> list: """Search for local businesses using Loop. Args: query: Natural-language description, e.g. 'vegan outdoor seating table for 4'. location: Area hint, default Kreuzberg, Berlin. Returns: List of matching business results with id, name, confidence, and availability. """ return requests.get(f"{BASE}/search", params={"q": query, "location": location}).json()["results"] def get_loop_details(result_id: str) -> dict: """Get full details for a Loop search result, including a result_token for report(). Args: result_id: id from a search result. Returns: Full record with attrs, restaurant sub-object, confidence, and result_token. """ return requests.get(f"{BASE}/details/{result_id}").json() def verify_loop(result_id: str, claim: str) -> dict: """Re-check a specific claim against live sources before acting. Args: result_id: id from a search result. claim: What to verify, e.g. 'is_open_now', 'has_outdoor_seating'. Returns: Observation with verified, confidence, and checked_at fields. """ return requests.get(f"{BASE}/verify/{result_id}", params={"claim": claim}).json() def report_loop(result_token: str, outcome: str) -> dict: """Report what happened after acting on a Loop result. Improves data for everyone. Args: result_token: From get_loop_details(), valid 30 minutes. outcome: One of correct, wrong, booked, closed, other. Returns: Confirmation with new_confidence score. """ return requests.post( f"{BASE}/report", json={"result_token": result_token, "outcome": outcome} ).json() tools = [FunctionTool.from_defaults(fn=f) for f in [search_loop, get_loop_details, verify_loop, report_loop]] agent = FunctionAgent(llm=OpenAI(model="gpt-4o"), tools=tools) async def main(): result = await agent.run(user_msg="Find a vegan restaurant with outdoor seating in Kreuzberg") print(str(result)) asyncio.run(main())
AgentWorkflow.from_tools_or_functions accepts plain callables alongside BaseTool objects. It wraps functions in FunctionTool automatically and selects FunctionAgent or ReActAgent based on whether the LLM supports function calling.
pip install llama-index llama-index-llms-openai requestsimport asyncio import requests from llama_index.core.agent.workflow import AgentWorkflow from llama_index.llms.openai import OpenAI BASE = "https://api.stayinloop.dev/v1" # Plain functions — AgentWorkflow wraps them in FunctionTool automatically def search_loop(query: str, location: str = "Kreuzberg, Berlin") -> list: """Search for local businesses using Loop.""" return requests.get(f"{BASE}/search", params={"q": query, "location": location}).json()["results"] def get_loop_details(result_id: str) -> dict: """Get full details for a Loop search result, including a result_token for report().""" return requests.get(f"{BASE}/details/{result_id}").json() def verify_loop(result_id: str, claim: str) -> dict: """Re-check a specific claim against live sources before acting.""" return requests.get(f"{BASE}/verify/{result_id}", params={"claim": claim}).json() def report_loop(result_token: str, outcome: str) -> dict: """Report what happened after acting on a Loop result. outcome: correct, wrong, booked, closed, other.""" return requests.post( f"{BASE}/report", json={"result_token": result_token, "outcome": outcome} ).json() agent = AgentWorkflow.from_tools_or_functions( [search_loop, get_loop_details, verify_loop, report_loop], llm=OpenAI(model="gpt-4o"), system_prompt="Use Loop tools to find and verify local businesses in Kreuzberg, Berlin.", ) async def main(): result = await agent.run(user_msg="Outdoor vegan table for 4 in Kreuzberg — verify it is open now") print(str(result)) asyncio.run(main())
https://stayinloop.dev/mcp. The four tools load automatically with no schema wrappers. See the Claude guide for the MCP connector path.Authorization: Bearer sk_live_… as a header or append ?key=sk_live_… to any URL.