DEV Community

Masumi Kawasaki 💭
Masumi Kawasaki 💭

Posted on

Building an MCP Server: Bringing WakaTime Data to AI Agents

Introduction

"How much did I code today?"
"What was my project breakdown last week?"

If you're a developer using WakaTime, these questions probably sound familiar. But opening a browser to check the dashboard feels like unnecessary friction.

I've been automating my daily retrospectives with AI, and having access to "how I spent my time" data makes those reflections much more accurate. While GitHub commit history is helpful, it doesn't capture the full picture—actual coding time, language breakdowns, project-by-project focus levels. If AI Agents could access WakaTime's rich data, the quality of automated retrospectives would improve dramatically.

So I built an MCP (Model Context Protocol) server to make WakaTime data directly accessible to AI Agents. Now agents can answer questions like "show me this week's coding time by project" and incorporate that data into retrospective reports.

This article walks through the implementation of wakatime-mcp and explains the key technical points of building an MCP server.

What is wakatime-mcp?

wakatime-mcp is an MCP server implementation that exposes WakaTime API functionality via the MCP protocol.

Features

It provides two tools:

1. wakatime_summaries
Fetches daily summaries for a specified date range, with optional project filtering and timezone support.

{
  "tool": "wakatime_summaries",
  "arguments": {
    "start": "2025-01-01",
    "end": "2025-01-07",
    "project": "my-project",
    "timezone": "Asia/Tokyo"
  }
}
Enter fullscreen mode Exit fullscreen mode

2. wakatime_today
Fetches today's summary. Defaults to Asia/Tokyo timezone.

{
  "tool": "wakatime_today",
  "arguments": {
    "project": "my-project",
    "timezone": "Asia/Tokyo"
  }
}
Enter fullscreen mode Exit fullscreen mode

No Installation Required

Using npx, you can run it directly without installation:

export WAKATIME_API_KEY="YOUR_API_KEY"
npx -y github:geeknees/wakatime-mcp
Enter fullscreen mode Exit fullscreen mode

To add it to your AI Agent's MCP configuration:

{
  "command": "npx",
  "args": ["-y", "github:geeknees/wakatime-mcp"],
  "env": {
    "WAKATIME_API_KEY": "YOUR_API_KEY"
  }
}
Enter fullscreen mode Exit fullscreen mode

You can get your WakaTime API Key from the WakaTime settings page.

Implementation Deep Dive

The entire implementation is about 170 lines of TypeScript—surprisingly concise. Here are the key technical points.

1. WakaTime API Authentication

WakaTime uses a slightly unusual authentication method. Instead of Bearer tokens, it requires Base64-encoding the API key and passing it as a Basic auth header:

export function basicAuthHeaderFromApiKey(apiKey: string): string {
  const b64 = Buffer.from(apiKey, "utf8").toString("base64");
  return `Basic ${b64}`;
}
Enter fullscreen mode Exit fullscreen mode

Simple, but easy to miss if you don't read the docs carefully.

2. MCP Protocol Implementation

The @modelcontextprotocol/sdk makes MCP server implementation remarkably straightforward. You basically register two handlers:

const server = new Server(
  { name: "wakatime-mcp", version: "0.0.1" },
  { capabilities: { tools: {} } }
);

// 1. Handler that returns available tools
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools: [
    {
      name: "wakatime_summaries",
      description: "Fetches WakaTime daily summaries for a date range",
      inputSchema: { /* JSON Schema */ }
    },
    // ...
  ]
}));

// 2. Handler that executes tool calls
server.setRequestHandler(CallToolRequestSchema, async (req) => {
  const { name, arguments: args } = req.params;
  // Tool execution logic
});
Enter fullscreen mode Exit fullscreen mode

The stdio communication is handled entirely by StdioServerTransport, so you don't need to worry about protocol details:

await server.connect(new StdioServerTransport());
Enter fullscreen mode Exit fullscreen mode

3. Timezone-Aware Date Handling

For the "today" summary, we need to account for timezones. Node.js's Intl.DateTimeFormat makes this straightforward:

export function todayYmd(tz = "Asia/Tokyo"): string {
  const parts = new Intl.DateTimeFormat("en-CA", {
    timeZone: tz,
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
  }).formatToParts(new Date());

  const y = parts.find((p) => p.type === "year")?.value;
  const m = parts.find((p) => p.type === "month")?.value;
  const d = parts.find((p) => p.type === "day")?.value;
  return `${y}-${m}-${d}`;
}
Enter fullscreen mode Exit fullscreen mode

Using the en-CA locale automatically gives us YYYY-MM-DD formatting—a nice little trick.

4. Symlink Support for npx Execution

When running via npx, the entry point is often invoked through a symlink. To handle this, we use realpath to resolve the actual file path:

export function shouldRunMain(importMetaUrl: string, argv1?: string): boolean {
  if (!argv1) return false;

  const argvUrl = pathToFileURL(argv1).href;
  if (importMetaUrl === argvUrl) return true;

  try {
    const argvRealUrl = pathToFileURL(realpathSync(argv1)).href;
    const importMetaRealPath = realpathSync(fileURLToPath(importMetaUrl));
    const importMetaRealUrl = pathToFileURL(importMetaRealPath).href;
    return importMetaRealUrl === argvRealUrl;
  } catch {
    return false;
  }
}
Enter fullscreen mode Exit fullscreen mode

Without this, npx github:geeknees/wakatime-mcp wouldn't trigger the main function. Small detail, but crucial.

5. Type-Safe Validation with Zod

Input validation uses Zod for runtime type checking:

const SummariesInput = z.object({
  start: z.string().min(1),
  end: z.string().min(1),
  project: z.string().optional(),
  timezone: z.string().optional(),
});

// Usage
const input = SummariesInput.parse(args ?? {});
Enter fullscreen mode Exit fullscreen mode

This gives you both runtime validation and TypeScript type inference—great developer experience.

Real-World Usage

Here's how it works in practice for automated retrospectives.

Daily Retrospectives

Combining GitHub commit history with WakaTime coding time provides much more accurate daily reflections:

AI Agent: Tell me about your work today

User: Create today's retrospective

AI Agent: (executes wakatime_today)
You coded for 5 hours 23 minutes today:
- TypeScript: 3h 45m
- Markdown: 1h 15m
- JSON: 23m

Main project was wakatime-mcp, and looking at your GitHub commits,
you were working on MCP implementation and documentation.
Enter fullscreen mode Exit fullscreen mode

Weekly Reports

It's also useful for weekly work analysis:

User: Generate last week's work summary

AI Agent: (executes wakatime_summaries)
Work time for January 13-19, 2025:
Total coding: 28h 15m

By project:
- wakatime-mcp: 12h 30m (new development)
- my-app: 8h 45m (feature additions)
- docs: 7h 00m (documentation)

By language:
- TypeScript: 18h 20m
- Python: 6h 30m
- Markdown: 3h 25m

The data clearly shows you focused on new MCP server
development last week.
Enter fullscreen mode Exit fullscreen mode

Integration with Custom Skills

When combined with custom skills like Obsidian Daily Summary, you can generate comprehensive retrospectives that include WakaTime data alongside GitHub activity and meeting notes, providing deeper insights.

Conclusion

Building an MCP server is simpler than you might think. With @modelcontextprotocol/sdk, you can create something functional in just a few hours. The key points:

  1. Understand the protocol: Once you grasp MCP basics, the SDK handles the heavy lifting
  2. Leverage existing APIs: Most services provide APIs—just expose them via MCP
  3. Practical use cases: AI Agent integration opens up new possibilities for data utilization

wakatime-mcp is publicly available as a general-purpose tool for any WakaTime user. Give it a try!

Links


Why not build an MCP server for a service you use? I hope this article helps you get started.

Top comments (0)