Skip to main content
Glama
EduardMcfly

mcp-invoices

by EduardMcfly
README.md
# mcp-hello

Hello world MCP server, structured just like [mcp](../mcp): `McpServer` from the official SDK, stdio/HTTP transports, and AWS Lambda deploy via the Serverless Framework. Also includes an invoices ("facturas") feature backed by DynamoDB, exposed both as MCP tools and as a REST API.

## Install

```bash
npm install
npm run build
```

## Local (stdio)

- `npm run mcp` — starts the server over stdio (`tsx src/server.ts`)

To connect it to Claude Code / Cursor, point the MCP client at the `npx tsx src/server.ts` command (or the compiled binary in `dist/server.js` after `npm run build`).

## Local (HTTP)

```bash
npm run mcp:http
```

- MCP: `http://localhost:8787/mcp`
- Health: `http://localhost:8787/health`
- Invoices REST API: `http://localhost:8787/invoices`

Requires AWS credentials with access to the `InvoicesTable` DynamoDB table (see [Invoices](#invoices) below). Set `INVOICES_TABLE_NAME` (and optionally `AWS_REGION` / `DYNAMODB_ENDPOINT` for DynamoDB Local) in your `.env`.

## Deploy (AWS Lambda)

```bash
npm run deploy -- --stage dev
npm run deploy:remove -- --stage dev
```

The deploy uses `serverless-esbuild` to bundle `lambdas/handler.ts` and exposes the MCP server through API Gateway (HTTP API). Once it finishes, `serverless deploy` prints the public URL (`https://<id>.execute-api.<region>.amazonaws.com/mcp`); add `serverless-domain-manager` and your own DNS config if you want a custom domain.

## Scripts

| Script | Description |
|--------|-------------|
| `npm run mcp` | MCP over stdio (local) |
| `npm run mcp:http` | MCP over HTTP (local) |
| `npm run build` | Compiles `src/` to `dist/` |
| `npm run ts:check` | Type-checks without emitting |
| `npm run deploy` | Lambda + API Gateway |
| `npm run deploy:remove` | Removes the deployed stack |

## Tools

- `say_hello({ name? })` → returns `Hello, <name>!` (or `Hello, world!` if no name is given).
- `save_invoice({ invoiceNumber?, date, provider, concept, currency, amount, status?, items? })` → creates an invoice in DynamoDB.
- `get_invoice({ id })` → fetches a single invoice by id.
- `list_invoices({ from?, to?, limit?, cursor? })` → lists invoices sorted by date (newest first), optionally filtered by date range (`YYYY-MM-DD`).

## Invoices

Invoices are stored in a DynamoDB table (`InvoicesTable`, provisioned in `serverless.yml`) with `id` as the primary key and a `byDate` global secondary index for date-range queries.

Fields: `id`, `invoiceNumber?`, `date` (ISO `YYYY-MM-DD`), `provider`, `concept`, `currency` (default `USD`), `amount`, `status` (`pending` | `paid`, default `pending`), `items?` (`{ description, quantity, unitPrice }[]`), `createdAt`, `updatedAt`.

### REST API

| Method & path | Description |
|---|---|
| `POST /invoices` | Create an invoice. Body matches the invoice fields above (minus `id`/`createdAt`/`updatedAt`). |
| `GET /invoices` | List invoices. Query params: `from`, `to` (ISO dates), `limit` (default 20, max 100), `cursor` (pagination token from a previous response). |
| `GET /invoices/:id` | Fetch a single invoice, 404 if not found. |

No authentication yet — add it before exposing this outside a trusted network.

## Architecture

```text
MCP client → src/server.ts (stdio) | src/index.ts / lambdas/handler.ts (HTTP) → src/register-server.ts → tools/
REST client → src/app.ts (Express) → src/routes/ → src/repositories/ → DynamoDB
```

| File | Role |
|------|------|
| `src/register-server.ts` | Factory that creates the `McpServer` and registers the tools |
| `src/server.ts` | Stdio entrypoint (Cursor/Claude Code, local) |
| `src/app.ts` | Express app with the Streamable HTTP transport (`/mcp`, `/health`, `/invoices`) |
| `src/index.ts` | Local HTTP entrypoint (`npm run mcp:http`) |
| `lambdas/handler.ts` | `@codegenie/serverless-express` wrapper for AWS Lambda |
| `src/tools/` | One `register*Tool` function per MCP tool |
| `src/routes/` | Express routers for the REST API |
| `src/repositories/` | DynamoDB data access (shared by MCP tools and REST routes) |
| `src/models/` | Zod schemas / types for domain entities |
| `src/db/dynamo-client.ts` | Shared `DynamoDBDocumentClient` + table/index names |