Skip to main content
Glama
README.md
# TIF Score MCP

An MCP (Model Context Protocol) server that exposes three tools to any MCP-compatible client (Claude Desktop, Claude Code, or anything else that speaks MCP):

- **`score_conversation`** — analyzes a call transcript and returns structured business intelligence.
- **`get_lead_score`** — scores a transcript 0-100 and classifies it hot / warm / cold.
- **`diagnose_workflow_error`** — given an n8n workflow's JSON and an error message, returns a root cause and a proposed minimal fix.

This is a port, not a rewrite: the scoring prompt and weighting logic are adapted from a real, working conversation-analysis and lead-scoring pipeline (company and product names generalized for this public repo), and the workflow-diagnosis tool is a scoped-down port of a larger workflow self-healing system's diagnostic reasoning. See [`docs/BTS.md`](docs/BTS.md) for a plain-language writeup of the whole process, from planning through finished server.

## Demo

Real MCP server, real Claude API calls, real output — no mocked data. Generated by running [`demo/run-demo.mjs`](demo/run-demo.mjs) (`npm run demo`):

![Demo: all three tools called over real stdio JSON-RPC, real Claude API responses](demo/demo.gif)

A produced walkthrough is also available: [`demo/promo/tif-score-mcp-promo.mp4`](demo/promo/tif-score-mcp-promo.mp4) (46s) — why this exists, the three tools, and the same real terminal footage above, narrated.

## Tools

### `score_conversation`

**Input:**
```json
{
  "transcript": "Agent: Thanks for calling...\nCaller: Hi, I need to book...",
  "call_duration": 184,
  "raw_outcome": "completed",
  "source_system": "Retell"
}
```

**Output:**
```json
{
  "intent": "booking",
  "sentiment": "positive",
  "urgency": "high",
  "objections": ["price"],
  "outcome": "booked",
  "topics": ["appointment booking", "pricing question"],
  "missed_opportunity": false,
  "follow_up_needed": false,
  "revenue_signal": "high",
  "churn_risk": false,
  "ai_summary": "The caller wanted a same-day appointment and asked about price before booking. They booked a 4pm slot despite the price being a bit higher than expected."
}
```

### `get_lead_score`

Same input shape as `score_conversation`, plus an optional `repeat_caller` boolean (see [Known limitation](#known-limitation-repeat_caller) below).

**Output:**
```json
{
  "score": 75,
  "tier": "hot",
  "signals_used": ["high urgency (+25)", "booking intent (+20)", "booked outcome (+20)", "positive sentiment (+10)"]
}
```

Scoring weights:

| Signal | Points |
|---|---|
| High urgency | +25 |
| Booking intent | +20 |
| Booked outcome | +20 |
| Repeat caller | +15 |
| Asked about pricing | +10 |
| Positive sentiment | +10 |
| Negative or frustrated sentiment | -15 |
| Dropped call | -10 |
| Low-intent inquiry | -10 |

Score is clamped to 0-100. Bands: 70-100 hot, 40-69 warm, 0-39 cold.

### `diagnose_workflow_error`

**Input:**
```json
{
  "workflowJson": { "name": "...", "nodes": [...], "connections": {...} },
  "errorMessage": "Cannot read properties of undefined (reading 'toUpperCase')"
}
```

**Output:**
```json
{
  "rootCause": "The Code node assumes $json.body.customer always exists, but the webhook can receive a payload without a customer object.",
  "fixSummary": "Add optional chaining so the node doesn't throw when customer is missing.",
  "fixable": true,
  "fixedWorkflow": { "...": "complete corrected workflow JSON" },
  "confidence": "medium",
  "notes": "Verify the caller of this node can tolerate a null email before deploying."
}
```

**Scope note:** this tool is a deliberately scoped-down port of a larger, stateful self-healing system. The original dispatches a separate evaluator sub-agent so the fix-generator never grades its own work, retries once on a rejected fix, fetches and writes back to a live n8n instance, and persists memory between runs. None of that fits inside one stateless MCP tool call, so only the diagnostic reasoning was ported: classify the error, propose the smallest possible fix, and admit honestly when a workflow edit can't fix it (expired credentials, rate limits, upstream outages, etc). Treat `fixedWorkflow` as a starting point for human or separate review, not something to auto-deploy.

### Known limitation: `repeat_caller`

The "repeat caller" signal (+15 points) can't be derived from a transcript's text alone — it requires knowing call history, which this stateless tool doesn't have access to. If you already know this from your own CRM or call log, pass `repeat_caller: true` explicitly; otherwise it defaults to `false` and that signal is simply not counted.

## Setup

```bash
npm install
cp .env.example .env
# edit .env and set ANTHROPIC_API_KEY
```

## Registering with an MCP client

Add this to your `.mcp.json` (or the equivalent config for your client), using an **absolute path** since `.mcp.json` doesn't reliably resolve relative paths across clients:

```json
{
  "mcpServers": {
    "tif-score-mcp": {
      "command": "node",
      "args": ["/absolute/path/to/tif-score-mcp/src/index.js"],
      "env": {
        "ANTHROPIC_API_KEY": "your-key-here"
      }
    }
  }
}
```

Restart your MCP client afterward so it picks up the new server.

## Testing

```bash
npm run test:unit    # pure scoring-math tests, no network, no API key needed
npm run test:client  # spawns the real server, calls all three tools over real stdio JSON-RPC — needs ANTHROPIC_API_KEY, makes real (billed) API calls
```

You can also poke all three tools interactively without writing any code:

```bash
npx @modelcontextprotocol/inspector node src/index.js
```

## A model compatibility note

This server defaults to `claude-sonnet-4-6` with `temperature: 0` for deterministic output. That's a deliberate choice: `claude-sonnet-4-6` is the newest model that still accepts the `temperature` parameter — newer models (Opus 4.7+, Fable 5) reject it outright. If you change `TIF_MODEL` to a newer model, remove `temperature` from the calls in `src/lib/anthropic.js` first, or every tool call will fail.

## Security

Never commit real API keys. `.env` is gitignored from the first commit, and `.mcp.json` in this repo only ever contains a placeholder — fill in your real key locally, not in version control.

## Project layout

See [`CLAUDE.md`](CLAUDE.md) for a fuller orientation, [`LEARNINGS.md`](LEARNINGS.md) for decisions made while building this, and [`SKILLS.md`](SKILLS.md) for which tools/skills this project leaned on.

## License

MIT — see [`LICENSE`](LICENSE).

TDQS

B3.4/5.0

Scored across 3 tools

Disambiguation3/5

get_lead_score and score_conversation both analyze call transcripts and clearly overlap — get_lead_score even notes it runs the same analysis as score_conversation. This creates real ambiguity about which to call. diagnose_workflow_error is completely distinct, but the two scoring tools blur boundaries significantly.

Naming Consistency3/5

Tools use a consistent verb_noun pattern (get_lead_score, diagnose_workflow_error, score_conversation) with snake_case throughout. The naming styles are readable and consistent, though the verbs are somewhat varied (get, diagnose, score). No mixing of case conventions or chaotic patterns.

Tool Count2/5

Three tools is at the thin end but not necessarily inappropriate. However, two of the three tools (get_lead_score and score_conversation) perform overlapping transcript analysis, making the actual distinct tool surface effectively two tools. The scope is unclear — the server mixes transcript scoring with completely unrelated n8n workflow diagnosis, suggesting no coherent singular purpose.

Completeness2/5

The transcript-analysis side lacks balance: it covers extraction and scoring but has no update, correction, or batch-processing capability. The n8n workflow tool appears to be an orphan — a single diagnostic tool with no companion tools for related functionality. The two unrelated domains each feel incomplete, and the overall surface has no clear lifecycle coverage.

Maintenance

ActivitySlowing
ResponsivenessNo issues