Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ Add to your MCP client config and start asking questions. The server scans your

The first call scans your local data (takes a moment). Subsequent calls within 60 seconds return instantly from the in-memory cache.

### Slash commands

Once connected, these are available as slash commands in your AI agent:

| Command | Description |
|---------|-------------|
| `/mcp__agentmeter__sessions` | Show recent sessions — token usage, duration, model. Optional: `limit` |
| `/mcp__agentmeter__spend` | Show spend summary and daily breakdown (requires account). Optional: `days` |

### Available tools (no account needed)

| Tool | Description |
Expand Down Expand Up @@ -107,6 +116,46 @@ Example questions unlocked with an account:

---

## Updating

**Using `npx` (default config):** npx caches the package. To always resolve the latest version, use `@latest` in your config args:

```json
{
"mcpServers": {
"agentmeter": {
"command": "npx",
"args": ["@agentmeter/cli@latest", "mcp"]
}
}
}
```

**Using a global install** (faster startup, explicit updates):

```bash
npm install -g @agentmeter/cli
```

Then use `agentmeter` as the command directly:

```json
{
"mcpServers": {
"agentmeter": {
"command": "agentmeter",
"args": ["mcp"]
}
}
}
```

To update: `npm install -g @agentmeter/cli@latest`

**After updating:** restart your MCP client (Claude Code, Cursor) — MCP servers are not hot-reloaded.

---

## CLI reference

The CLI is optional for MCP usage but required for background auto-sync and for submitting sessions to AgentMeter.
Expand Down
54 changes: 53 additions & 1 deletion packages/cli/src/commands/mcp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ export async function handleGetTeamSpend({
async function startMcpServer(): Promise<void> {
const server = new McpServer(
{ name: 'agentmeter', version: '1.0.0' },
{ capabilities: { tools: {} } },
{ capabilities: { prompts: {}, tools: {} } },
);

server.tool(
Expand Down Expand Up @@ -465,6 +465,58 @@ async function startMcpServer(): Promise<void> {
async ({ days = 30 }) => handleGetTeamSpend({ days }),
);

// -------------------------------------------------------------------------
// Prompts — surfaced as slash commands in MCP clients (e.g. /mcp__agentmeter__sessions)
// -------------------------------------------------------------------------

server.prompt(
'sessions',
'Show your recent AI coding sessions — token usage, duration, model, and repo',
{
limit: z.string().optional().describe('Number of sessions to show (1–50, default 10)'),
},
async ({ limit }) => {
const limitNum = limit ? Math.min(50, Math.max(1, Number.parseInt(limit, 10) || 10)) : 10;
const result = await handleListRecentSessions({ limit: limitNum });
const data = result.content[0]?.text ?? '{}';
return {
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: `Here are my ${limitNum} most recent AI coding sessions:\n\n${data}\n\nPlease summarise what I have been working on, call out any sessions that used an unusually high number of tokens, and give me the total token count across all sessions shown.`,
},
},
],
};
},
Comment on lines +478 to +493

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing error handlinghandleListRecentSessions involves file I/O and can throw. Per CLAUDE.md: "the CLI should never crash with an unhandled exception. Catch, log, continue."

Wrap the handler body in try/catch and return a graceful message on failure:

async ({ limit }) => {
  try {
    const limitNum = limit ? Math.min(50, Math.max(1, Number.parseInt(limit, 10) || 10)) : 10;
    const result = await handleListRecentSessions({ limit: limitNum });
    const data = result.content[0]?.text ?? 'No session data available.';
    return {
      messages: [{
        role: 'user' as const,
        content: { type: 'text' as const, text: `Here are my \$\{limitNum} most recent AI coding sessions:\n\n\$\{data}\n\n...` },
      }],
    };
  } catch (err) {
    return {
      messages: [{
        role: 'user' as const,
        content: { type: 'text' as const, text: `Failed to fetch sessions: \$\{String(err)}` },
      }],
    };
  }
},

);

server.prompt(
'spend',
'Show your AI coding spend summary and daily breakdown (requires AgentMeter account)',
{
days: z.string().optional().describe('Number of days to look back (default 7)'),
},
async ({ days }) => {
const daysNum = days ? Math.min(365, Math.max(1, Number.parseInt(days, 10) || 7)) : 7;
const result = await handleGetMySpend({ days: daysNum });
const data = result.content[0]?.text ?? '{}';
return {
messages: [
{
role: 'user' as const,
content: {
type: 'text' as const,
text: `Here is my AI coding spend data for the last ${daysNum} day${daysNum === 1 ? '' : 's'}:\n\n${data}\n\nPlease summarise my spending trends, highlight any notable spikes or patterns, and give me a sense of whether my usage is tracking high or low.`,
},
},
],
};
},
Comment on lines +502 to +517

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same missing error handling as the sessions handler — handleGetMySpend makes a network call and can throw. Same fix applies: wrap in try/catch and return a message on error rather than letting it propagate.

);

const transport = new StdioServerTransport();
await server.connect(transport);
}
Expand Down
Loading