Skip to main content
Glama
limjonathan

runtime-mcp-connect

by limjonathan
README.md
<div align="center">

# runtime-mcp

**Your app's live runtime state — logs, DB schema, config, feature flags — as MCP tools for coding agents.**

[![npm core](https://img.shields.io/npm/v/runtime-mcp.svg?style=flat-square&color=cb3837&label=npm%20%7C%20core)](https://www.npmjs.com/package/runtime-mcp)
[![npm integrations](https://img.shields.io/npm/v/runtime-mcp-integrations.svg?style=flat-square&color=cb3837&label=%20integrations)](https://www.npmjs.com/package/runtime-mcp-integrations)
[![npm connect](https://img.shields.io/npm/v/runtime-mcp-connect.svg?style=flat-square&color=cb3837&label=%20connect)](https://www.npmjs.com/package/runtime-mcp-connect)
[![node](https://img.shields.io/node/v/runtime-mcp.svg?style=flat-square&color=339933)](package.json)
[![tests](https://img.shields.io/badge/tests-39%20passing-brightgreen.svg?style=flat-square)](#development)
[![license](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](LICENSE)

*Drop it into your app. Your agent stops guessing.*

</div>

---

When opencode, Claude Code, or Cursor debug your app today, they work blind: stale guesses about your schema, pasted log fragments, invented config values.

**runtime-mcp** closes that gap. Two lines of setup and any MCP-speaking agent can query what your *running process* actually sees:

```txt
Agent: "why is checkout failing?"
  ├─ get_errors   → TypeError at checkout.ts:42, full stack trace
  ├─ db_query     → orders table has no `coupon_id` column
  ├─ config://    → STRIPE_KEY: [REDACTED], TAX_RATE: 0  ← there it is
  └─ "Found it — you renamed the env var."
```

## Quickstart

**0 — Install:**

```bash
npm install runtime-mcp runtime-mcp-integrations
```

**1 — Instrument your app** (one import):

```ts
import { runtimeMcp } from 'runtime-mcp';
import { pgAdapter } from 'runtime-mcp-integrations/pg';

await runtimeMcp({
  db: pgAdapter(pool),
  config: process.env,
  flags: myFlagRegistry,
});
```

**2 — Register the shim** in your project's `.mcp.json`:

```json
{
  "mcpServers": {
    "runtime": { "command": "npx", "args": ["-y", "runtime-mcp-connect"] }
  }
}
```

**3 — Start your app.** That's it. The agent connects automatically and sees live state from then on.

## What agents get

#### Tools

| Tool | Description |
|---|---|
| `get_logs` | Recent logs · filter by level, free-text query, time window |
| `get_errors` | Error entries with stack traces — first call when something broke |
| `db_query` | Read-only SQL (`SELECT`/`WITH` only) · writes rejected · auto `LIMIT` |
| `app_info` | Name, version, PID, uptime, registered routes |

#### Resources

| Resource | Description |
|---|---|
| `config://current` | Live config with secrets replaced by `[REDACTED]` |
| `flags://state` | Feature flag states per environment |
| `schema://tables` | Table inventory |
| `schema://table/{name}` | Columns, primary keys, indexes, foreign keys |

## Architecture

```
┌──────────────────────────────┐          ┌─────────────────────────────┐
│         Your App             │          │   Agent (opencode/Claude)   │
│                              │          │                             │
│  runtimeMcp({ db, config })  │          │  .mcp.json                  │
│   ├─ log ring buffer         │          │    └─ runtime-mcp-connect   │
│   ├─ schema introspection    │          │          │ stdio            │
│   ├─ config + redaction      │◄─────────┼──────────┘                  │
│   └─ feature flags           │  loopback│                             │
│                              │   HTTP   │                             │
│  127.0.0.1:<port>/mcp        │          │                             │
│  .runtime-mcp.json ──────────┼─ pid·url·token·(chmod 600)             │
└──────────────────────────────┘          └─────────────────────────────┘
```

**Why two pieces?** Agents spawn their own processes — they can't reach into your app. The discovery file bridges them: the library writes it on boot, the shim reads it on every call. App restarted with a new port or token? The shim reconnects automatically. Nothing to reconfigure, ever.

## Adapters

```bash
npm install runtime-mcp runtime-mcp-integrations
```

<table>
<tr><th>Import</th><th>Use with</th></tr>
<tr><td><code>runtime-mcp-integrations/pg</code></td><td><code>pg</code> Pool — full introspection + queries</td></tr>
<tr><td><code>runtime-mcp-integrations/prisma</code></td><td>PrismaClient (Postgres)</td></tr>
<tr><td><code>runtime-mcp-integrations/node-sqlite</code></td><td><code>node:sqlite</code> DatabaseSync — zero native deps</td></tr>
<tr><td><code>runtime-mcp-integrations/pino</code></td><td>Pino destination mirroring into the buffer</td></tr>
<tr><td><code>runtime-mcp-integrations/winston</code></td><td>Winston transport</td></tr>
<tr><td><code>runtime-mcp-integrations/express</code></td><td>Error middleware recording request failures</td></tr>
</table>

Structured logging example:

```ts
import pino from 'pino';
import { createRuntimeMcpPinoDestination } from 'runtime-mcp-integrations/pino';

const logger = pino(pino.multistream([
  pino.destination(1),
  createRuntimeMcpPinoDestination(),
]));
```

No logger wired up? Console capture is on by default — `console.error` and friends flow into the same buffer.

## Security model

Built for local development and staging. Defense in depth by default:

| Control | Behavior |
|---|---|
| Loopback bind | Endpoint listens on `127.0.0.1` only; remote exposure requires explicit opt-in |
| Token auth | Bearer token per app start, stored chmod 600 in `.runtime-mcp.json` |
| Read-only SQL | Allowlist parser: non-`SELECT` rejected, stacked statements rejected, keywords inside strings/comments ignored |
| Secret redaction | Password/token/key/auth-shaped keys and known token formats become `[REDACTED]` before leaving the process |
| No telemetry | Zero phone-home |

Add `.runtime-mcp.json` to `.gitignore`.

## Why now

| Existing solution | Limitation |
|---|---|
| Next.js 16 `/_next/mcp` | Framework-locked |
| Vercel runtime logs MCP | Cloud platform only |
| Datadog / Sentry / Supabase MCPs | SaaS accounts; not your live dev process |

Every platform is building this inward. **Nobody built it generically.** This is that.

## Demo

`packages/demo-app` is an Express + SQLite store with two planted bugs — an N+1 query on `/orders`, and a misnamed `DEMO_TAX_RATE` env var that silently zeroes checkout tax. Point your agent at it and ask *"why are checkout totals wrong?"*

```bash
pnpm install && pnpm -r build
pnpm --filter @runtime-mcp/demo-app dev
```

## Packages

All packages are published to npm (v0.1.0):

| Package | npm | Purpose |
|---|---|---|
|[`runtime-mcp`](packages/core) | [npm ↗](https://www.npmjs.com/package/runtime-mcp) | In-process registry · loopback endpoint · discovery · redaction |
|[`runtime-mcp-connect`](packages/connect) | [npm ↗](https://www.npmjs.com/package/runtime-mcp-connect) | stdio shim agents register in `.mcp.json` |
|[`runtime-mcp-integrations`](packages/integrations) | [npm ↗](https://www.npmjs.com/package/runtime-mcp-integrations) | Framework, logger, and database adapters |
|[`@runtime-mcp/demo-app`](packages/demo-app) | — | Debugging demo with planted bugs (repo only) |

Requires Node ≥ 20. ESM.

## Development

```bash
pnpm install
pnpm -r build
pnpm test    # unit + end-to-end over real HTTP and stdio transports
```

<details>
<summary><strong>Roadmap</strong></summary>

- [ ] Live log tailing via MCP resource subscriptions
- [ ] Fastify / Hono adapters
- [ ] MySQL / Drizzle introspection adapters
- [ ] Python SDK parity (FastAPI / Django)
- [ ] Multi-project workspaces in the connect shim

</details>

## License

[MIT](LICENSE)

Maintenance

ActivityMaintained
ResponsivenessNo issues