Efficiently tracking time and managing projects is essential for businesses of all sizes and having a good sense of the data is ever more important. Harvest is a popular time tracking tool used by thousands of companies, but what if you could interact with your time tracking data through natural language conversations?
Let's explore how to build a Model Context Protocol (MCP) server that connects Claude (or any other MCP-compatible large language model) with the Harvest API. This integration allows users to query time entries, manage projects, and track time through simple conversational prompts.
The Model Context Protocol is an open standard that enables seamless communication between large language models (LLMs) like Claude and external data sources or tools. MCP provides a standardized way for LLMs to access external context, execute functions, and return results to users.
By using MCP, we can give Claude the ability to interact with our Harvest time tracking data through natural language. Users can simply ask questions like "How many hours did our team log last week?" or "Create a time entry for my current project," and Claude can handle these requests by calling the appropriate Harvest API endpoints.
Let's build an MCP server that integrates with the Harvest API. We'll use Python with the official MCP SDK to create our server.
Before we begin, you'll need:
1. A Harvest account with API access
2. Python 3.10 or higher
3. Claude for Desktop (or another MCP-compatible client)
Here's the core of our Harvest MCP server:
async def harvest_request(path, params=None, method="GET"):
if not HARVEST_ACCOUNT_ID or not HARVEST_API_KEY:
raise ValueError("Missing Harvest API credentials. Set HARVEST_ACCOUNT_ID and HARVEST_API_KEY environment variables.")
headers = {
"Harvest-Account-Id": HARVEST_ACCOUNT_ID,
"Authorization": f"Bearer {HARVEST_API_KEY}",
"User-Agent": "Harvest MCP Server",
"Content-Type": "application/json"
}
url = f"https://api.harvestapp.com/v2/{path}"
async with httpx.AsyncClient() as client:
if method == "GET":
response = await client.get(url, headers=headers, params=params)
else:
response = await client.request(method, url, headers=headers, json=params)
if response.status_code != 200:
raise Exception(f"Harvest API Error: {response.status_code} {response.text}")
return response.json()
This code sets up our server and creates a helper function for making authenticated requests to the Harvest API.
Next, we'll define tools that can be invoked by Claude. Each tool maps to a specific Harvest API endpoint:
We've created two essential tools:
- list_users
- Gets all users in the Harvest account
- list_time_entries
- Gets time entries with flexible filtering options
Let's add tools for creating and managing time entries:
Finally, we'll add the code to run our server:
To connect our server to Claude for Desktop, we need to update the `claude_desktop_config.json` file:
Make sure to replace `/path/to/harvest_server.py` with the actual path to your server script and add your Harvest API credentials.
After you've configured Claude Desktop, you can start asking questions like:
"How many hours has Mikko Harju clocked this week?"
"Describe our company, our staff and their roles"
Of course, this does not end here. You can also try out summarizing tasks that people have done in a given project this week, what projects did certain individuals contribute to and other interesting tidbits you can imagine.
And extending that either by improving the server we implemented above or with aid from other tools, your imagination is the only limiting factor.
Happy time tracking!