tracetify-mcp
Advanced tools
+1
-1
| { | ||
| "name": "tracetify-mcp", | ||
| "mcpName": "io.github.tracetify/tracetify-mcp", | ||
| "version": "0.1.3", | ||
| "version": "0.2.0", | ||
| "description": "MCP server for Tracetify — trace how any product actually grew, from inside Claude Code or Cursor.", | ||
@@ -6,0 +6,0 @@ "keywords": [ |
+1
-0
@@ -39,2 +39,3 @@ # tracetify-mcp | ||
| | `get_trace` | free | Poll a running trace | | ||
| | `unlock_report` | credits | Permanently unlock a report's full timeline, evidence & SEO detail (idempotent — never charges twice) | | ||
@@ -41,0 +42,0 @@ Fresh traces draw from your Tracetify credit balance — the same balance the |
+16
-1
@@ -13,3 +13,3 @@ /** | ||
| export function createServer({ apiKey, baseUrl = DEFAULT_BASE, fetchImpl = fetch } = {}) { | ||
| const server = new McpServer({ name: 'tracetify', version: '0.1.0' }); | ||
| const server = new McpServer({ name: 'tracetify', version: '0.2.0' }); | ||
@@ -81,2 +81,17 @@ async function call(path, init = {}) { | ||
| server.registerTool( | ||
| 'unlock_report', | ||
| { | ||
| description: | ||
| "Permanently unlock a report's full timeline, evidence and SEO detail for this account. " | ||
| + "Costs credits — quote the exact price to the user first (it is in the report's " | ||
| + "timelineLocked.cost field from read_report) and call this ONLY after they explicitly " | ||
| + "agree to spend. Idempotent: unlocking an already-unlocked report never charges twice. " | ||
| + "Returns the full report. The verdict stays on the website — you are the analyst here.", | ||
| inputSchema: { slug: z.string() }, | ||
| }, | ||
| async ({ slug }) => | ||
| text(await call(`/api/mcp/v1/reports/${encodeURIComponent(slug)}/unlock`, { method: 'POST' })) | ||
| ); | ||
| server.registerTool( | ||
| 'get_trace', | ||
@@ -83,0 +98,0 @@ { |
+16
-2
@@ -19,3 +19,3 @@ import { describe, expect, it, vi } from 'vitest'; | ||
| expect(tools.map((t) => t.name).sort()).toEqual( | ||
| ['get_trace', 'read_report', 'search_reports', 'start_trace'] | ||
| ['get_trace', 'read_report', 'search_reports', 'start_trace', 'unlock_report'] | ||
| ); | ||
@@ -72,3 +72,3 @@ }); | ||
| const { tools } = await client.listTools(); | ||
| expect(tools).toHaveLength(4); | ||
| expect(tools).toHaveLength(5); | ||
| const res = await client.callTool({ name: 'search_reports', arguments: { query: 'x' } }); | ||
@@ -79,2 +79,16 @@ expect(res.isError).toBe(true); | ||
| }); | ||
| it('unlock_report posts to the unlock endpoint', async () => { | ||
| const fetchImpl = vi.fn().mockResolvedValue({ | ||
| ok: true, | ||
| json: async () => ({ charged: true, balance: 219, report: { host: 'weshop.ai' } }), | ||
| }); | ||
| const client = await connected(fetchImpl); | ||
| const res = await client.callTool({ name: 'unlock_report', arguments: { slug: 'weshop-ai' } }); | ||
| expect(fetchImpl).toHaveBeenCalledWith( | ||
| 'https://api.test/api/mcp/v1/reports/weshop-ai/unlock', | ||
| expect.objectContaining({ method: 'POST' }) | ||
| ); | ||
| expect(res.content[0].text).toContain('"charged": true'); | ||
| }); | ||
| }); |
12508
13.62%193
16.27%48
2.13%