Skip to main content
Glama

kelo-mcp

A small Model Context Protocol server that lets a Claude user ask what their business is owed, who to chase first, and get a chase email drafted in their own voice.

It cannot send anything. Every tool either reads or returns a draft. That is a product decision, not an oversight, and DESIGN.md explains what it costs and what it buys.

Extracted from Kelo, where it backs a live MCP endpoint. No SDK dependency: the JSON-RPC layer is hand-rolled, about 120 lines, and has no runtime dependencies at all.

Try it in 30 seconds

npm install
npm run example
curl -s localhost:8787 -H 'authorization: Bearer demo-key' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"get_weekly_number","arguments":{}}}'
{
  "verified_owed": "£6,050",
  "estimate_at_risk": "£9,600",
  "open_cases": 3,
  "note": "Verified is real overdue invoices. Estimate is quotes and enquiries, and is less certain."
}

That runs against an in-memory demo backend. No database, no Kelo account, no API key beyond the literal string demo-key.

Related MCP server: Xero MCP Server

The tools

Tool

Returns

get_weekly_number

Total owed, split into verified invoices and less certain estimates

get_chase_queue

Who to chase first, sorted by priority, value or age

get_customer

One customer's outstanding total, payment history and escalation status

draft_chase

A subject and body for one case. Returns text; never sends

get_due_soon

Invoices due within two weeks, ranked by how likely the client is to pay late

get_insights

Overdue total, average days overdue, aging buckets, worst payers

Wiring it to your own data

The protocol layer knows nothing about invoices. Implement ToolBackend and pass it in:

import { createPostHandler, callTool, TOOL_DEFS, SERVER_INFO, INSTRUCTIONS } from 'kelo-mcp'

export const POST = createPostHandler({
  config: { serverInfo: SERVER_INFO, tools: TOOL_DEFS, instructions: INSTRUCTIONS, callTool },
  resolveKey: async token => {
    const row = await db.apiKeys.findByHash(sha256(token))
    return row && !row.revoked_at ? { workspaceId: row.workspace_id } : null
  },
  makeContext: workspaceId => ({ workspaceId, backend: myBackend }),
})

createPostHandler takes and returns Web-standard Request/Response, so it drops into Next.js route handlers, Cloudflare Workers, Deno, Bun or plain Node 18+ without adaptation.

Layout

src/protocol.ts      JSON-RPC and MCP methods. No product knowledge, no dependencies.
src/tools.ts         Tool definitions: the surface a model actually sees.
src/backend.ts       The seam. Dispatch, formatting, and the ToolBackend interface.
src/demo-backend.ts  In-memory data so the thing runs out of the box.
src/http.ts          Bearer auth and the stateless POST transport.

The split exists so the protocol can be tested without a database. All 18 tests run in well under a second and touch nothing external.

npm test

What is not here

The live Kelo server backs these tools with Postgres queries, a drafting service and a billing entitlement check. That is Kelo's business logic and it is not in this repo. What is here is the protocol layer, the transport, the tool surface, and a demo backend that satisfies the same interface.

Notes on the protocol

Three things the spec asks for that are easy to get subtly wrong, and how this handles them:

  • Notifications get silence, not a response. A message with no id returns null internally and is dropped. If a batch was entirely notifications, the transport answers 202 with an empty body rather than 200 and [].

  • An unknown tool is a tool error, not a protocol error. It comes back as a normal result with isError: true so the model can read it and pick another tool. Reserving -32601 for genuinely unknown methods keeps the two failure modes distinct.

  • The client's protocol version is echoed when it sends one, rather than forcing the server's own.

Licence

MIT

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to interact with QuickFile UK accounting software, providing access to invoicing, client management, purchases, banking, and financial reporting through 40+ tools covering the complete QuickFile API.
    429
    4
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables interaction with the Xero Accounting API to manage contacts, invoices, payments, accounts, and financial reports. It provides a suite of tools for natural language access to accounting records and business performance data.
    20
    1
    Apache 2.0
  • A
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables AI assistants to interact with Conta Azul Financial APIs to manage accounts, balances, and transactions through natural language. It features specialized tools for tracking cash flow, processing payables and receivables, and generating comprehensive financial reports.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides structured, read-mostly access to small-business back-office data including customers, invoices, and account notes, allowing Claude to query overdue invoices, revenue summaries, and more.
    MIT