Skip to main content
Glama
Nelev
by Nelev
README.md
# mcp-agent-app

A TypeScript service that converts **GeoJSON geometries into 3D triangulated meshes** using a local LLM (Ollama). It exposes an MCP tool (`generate_mesh`) and an HTTP API, making mesh generation accessible both to MCP-compatible clients and standard REST consumers.

```
POST /generate-mesh
        │
        ▼
  Express API (src/api/index.ts)
        │
        ▼
  generateMeshAgent (src/agents/generate_mesh_agent.ts)
        │
        ▼
  Ollama LLM  ──────────────────────────────────────────>  MeshData
  (llama3.2 @ localhost:11434)                             { vertices, indices, metadata }

MCP server (src/server.ts) exposes the same agent as an MCP tool over stdio
```

## Stack

| Layer | Technology |
|---|---|
| Language | TypeScript 5 (strict, ES2022, ESM) |
| Runtime | Node.js 18+ with `tsx` for development |
| Web framework | Express 4 |
| MCP SDK | `@modelcontextprotocol/sdk` v1.29, `@modelcontextprotocol/server` v2.0 alpha |
| Schema validation | Zod |
| Local LLM | Ollama (`llama3.2` at `http://localhost:11434`) |

> **Note:** The MCP server and HTTP API use Ollama for inference — no Anthropic API key is required for the core mesh generation. `ANTHROPIC_API_KEY` is only needed if you extend the agent loop with Claude tool-use.

## Project structure

```
src/
├── server.ts                     # MCP server — registers and runs the generate_mesh tool
├── agents/
│   └── generate_mesh_agent.ts    # Core logic: GeoJSON → Ollama → MeshData
└── api/
    └── index.ts                  # Express HTTP API wrapping the agent
```

## Prerequisites

1. **Node.js 18+**
2. **Ollama** running locally with the `llama3.2` model pulled:
   ```bash
   ollama pull llama3.2
   ollama serve          # must be running on http://localhost:11434
   ```

## Setup

```bash
npm install
```

## Run (dev, no build step)

```bash
npm run dev    # starts the Express API on port 3000 (default)
```

In another terminal:

```bash
curl -X POST http://localhost:3000/generate-mesh \
  -H "Content-Type: application/json" \
  -d '{"geometry": "{\"type\":\"Polygon\",\"coordinates\":[[[0,0],[1,0],[1,1],[0,1],[0,0]]]}"}'
```

The `geometry` field accepts either a **JSON string** or a **parsed GeoJSON object** (Geometry or Feature).

Expected response shape:

```json
{
  "vertices": [0, 0, 0, 1, 0, 0, ...],
  "indices":  [0, 1, 2, ...],
  "metadata": {
    "featureType": "Polygon",
    "vertexCount": 8,
    "triangleCount": 4
  }
}
```

### Mesh conventions

| Geometry type | Mesh output |
|---|---|
| Polygon / MultiPolygon | Flat base (z=0) + extruded top (z=10) + side walls |
| LineString | Ribbon mesh, width 0.0001° |
| Point | Small pyramid |

Coordinates map as: longitude → x, latitude → y, elevation → z.

## Build & run (production)

```bash
npm run build
npm start
```

## Run the MCP server standalone

```bash
npm run mcp
# or inspect it interactively:
npx @modelcontextprotocol/inspector npx tsx src/server.ts
```

The MCP server exposes one tool — `generate_mesh` — that accepts a GeoJSON geometry or Feature as a JSON string and returns the same `MeshData` structure as the HTTP API.

## NPM scripts

| Script | Description |
|---|---|
| `npm run dev` | Start API in watch mode via `tsx` |
| `npm start` | Run compiled `dist/api/index.js` |
| `npm run build` | Compile TypeScript to `dist/` |
| `npm run mcp` | Run the MCP server standalone |

## Environment variables

| Variable | Default | Description |
|---|---|---|
| `PORT` | `3000` | Express server port |
| `CORS_ORIGINS` | `http://localhost:5173` | Comma-separated list of allowed CORS origins |

## Notes

- The MCP server logs to **stderr**, never stdout — stdout is the stdio protocol channel.
- Ollama responses are validated with a Zod schema; a regex fallback handles markdown-wrapped JSON output.
- The HTTP API allows requests with no `Origin` header (server-to-server calls) in addition to the CORS allowlist.
- For production: TLS at a reverse proxy, auth + rate limiting in front of `/generate-mesh`, and request timeouts via `AbortController`.
- To swap in a remote MCP server, replace `StdioClientTransport` with `StreamableHTTPClientTransport` in the MCP manager.