The Model Context Protocol (MCP) lets AI agents call external tools through a standardized interface. A multi-tool MCP agent can combine web search, file system access, database queries, and custom APIs in a single conversation. This tutorial builds an MCP agent with a search tool backed by the Scavio API, a file tool, and a calculator tool. The pattern scales to any number of tools. Search calls cost $0.005 each via Scavio.
Prerequisites
- Python 3.10+ installed
- mcp package installed (pip install mcp)
- A Scavio API key from scavio.dev
- Basic understanding of tool-use patterns
Walkthrough
Step 1: Define the MCP search tool server
Create an MCP tool server that exposes a web_search function. This server handles the API call and returns structured results to the agent.
from mcp.server import Server
from mcp.types import Tool, TextContent
import requests, os, json
server = Server('search-tools')
API_KEY = os.environ['SCAVIO_API_KEY']
@server.tool()
async def web_search(query: str, country: str = 'us') -> str:
"""Search the web for current information."""
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': country})
results = resp.json().get('organic_results', [])[:5]
return json.dumps([{'title': r['title'], 'url': r['link'],
'snippet': r.get('snippet', '')} for r in results], indent=2)Step 2: Add a TikTok search tool to the same server
Add a second tool that searches TikTok. Both tools share the same MCP server, so the agent can use either one in a single session.
@server.tool()
async def tiktok_search(keyword: str, count: int = 10) -> str:
"""Search TikTok for videos about a topic."""
resp = requests.post('https://api.scavio.dev/api/v1/tiktok/search/videos',
headers={'Authorization': f'Bearer {API_KEY}',
'Content-Type': 'application/json'},
json={'keyword': keyword, 'count': count, 'cursor': 0})
videos = resp.json().get('data', {}).get('videos', [])
return json.dumps([{'author': v.get('author', {}).get('nickname', ''),
'desc': v.get('desc', ''), 'plays': v.get('stats', {}).get('playCount', 0)}
for v in videos], indent=2)Step 3: Add a calculator and file reader tool
Demonstrate the multi-tool pattern by adding non-search tools. The agent decides which tool to use based on the user query.
import math
@server.tool()
async def calculate(expression: str) -> str:
"""Evaluate a mathematical expression safely."""
allowed = set('0123456789+-*/.() ')
if not all(c in allowed for c in expression):
return 'Error: only numeric expressions allowed'
try:
result = eval(expression)
return str(result)
except Exception as e:
return f'Error: {e}'
@server.tool()
async def read_file(path: str) -> str:
"""Read a local text file."""
try:
with open(path, 'r') as f:
content = f.read(10000) # limit to 10KB
return content
except FileNotFoundError:
return f'File not found: {path}'Step 4: Configure the MCP server manifest
Create the .mcp.json configuration file that tells the MCP client where to find your tool server and what tools are available.
// .mcp.json
{
"mcpServers": {
"search-tools": {
"command": "python",
"args": ["search_tools_server.py"],
"env": {
"SCAVIO_API_KEY": "your_scavio_api_key"
}
}
}
}
// The server exposes: web_search, tiktok_search, calculate, read_fileStep 5: Run the MCP server
Start the server with stdio transport. Any MCP-compatible client (Claude Desktop, Cursor, custom agents) can now connect and use all four tools.
import asyncio
from mcp.server.stdio import stdio_server
async def main():
async with stdio_server() as (read_stream, write_stream):
await server.run(read_stream, write_stream)
if __name__ == '__main__':
asyncio.run(main())
# Test manually:
# echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | python search_tools_server.py
# Should list: web_search, tiktok_search, calculate, read_filePython Example
from mcp.server import Server
from mcp.server.stdio import stdio_server
import requests, os, json, asyncio
server = Server('search-tools')
API_KEY = os.environ['SCAVIO_API_KEY']
@server.tool()
async def web_search(query: str, country: str = 'us') -> str:
"""Search the web for current information."""
resp = requests.post('https://api.scavio.dev/api/v1/search',
headers={'x-api-key': API_KEY, 'Content-Type': 'application/json'},
json={'query': query, 'country_code': country})
results = resp.json().get('organic_results', [])[:5]
return json.dumps([{'title': r['title'], 'snippet': r.get('snippet', '')} for r in results])
@server.tool()
async def tiktok_search(keyword: str, count: int = 10) -> str:
"""Search TikTok videos."""
resp = requests.post('https://api.scavio.dev/api/v1/tiktok/search/videos',
headers={'Authorization': f'Bearer {API_KEY}', 'Content-Type': 'application/json'},
json={'keyword': keyword, 'count': count, 'cursor': 0})
return json.dumps(resp.json().get('data', {}).get('videos', [])[:5])
async def main():
async with stdio_server() as (read, write):
await server.run(read, write)
if __name__ == '__main__':
asyncio.run(main())JavaScript Example
// MCP servers are typically Python; this shows a JS HTTP wrapper
const API_KEY = process.env.SCAVIO_API_KEY;
const tools = {
async web_search({ query, country = 'us' }) {
const resp = await fetch('https://api.scavio.dev/api/v1/search', {
method: 'POST',
headers: { 'x-api-key': API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ query, country_code: country })
});
const data = await resp.json();
return (data.organic_results || []).slice(0, 5)
.map(r => ({ title: r.title, snippet: r.snippet || '' }));
},
async tiktok_search({ keyword, count = 10 }) {
const resp = await fetch('https://api.scavio.dev/api/v1/tiktok/search/videos', {
method: 'POST',
headers: { 'Authorization': `Bearer ${API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ keyword, count, cursor: 0 })
});
return (await resp.json()).data?.videos || [];
}
};
async function main() {
const results = await tools.web_search({ query: 'MCP protocol 2026' });
console.log(JSON.stringify(results, null, 2));
}
main().catch(console.error);Expected Output
Available tools:
- web_search: Search the web for current information
- tiktok_search: Search TikTok for videos about a topic
- calculate: Evaluate a mathematical expression
- read_file: Read a local text file
[
{"title": "MCP Protocol Documentation", "snippet": "The Model Context Protocol..."}
{"title": "Building MCP Agents in 2026", "snippet": "A guide to creating..."}
]
Cost: $0.005 per web_search call, $0.005 per tiktok_search call