Skip to main content
Glama
Ovidio-Git

Public Portfolio MCP

by Ovidio-Git
README.md
# Public Portfolio MCP

A small, production-minded read-only JSON-RPC service for exposing a deliberately bounded engineering portfolio to recruiters and external agents.

The interesting part is not the profile data. It is the boundary: callers can invoke three purpose-built tools, but they cannot choose a database table, filesystem path, URL, command, or private application module.

## What this demonstrates

- Strict TypeScript and zero production dependencies
- MCP-style `initialize`, `tools/list`, and `tools/call` over JSON-RPC 2.0
- Fixed read-only tool allowlist with exact argument keys
- Payload limits, rate limiting, CORS allowlisting, and security headers
- Bounded public text and prompt-injection-shaped input rejection
- Stable errors without stack traces or request payload logging
- Tests, Docker, GitHub Actions, architecture notes, ADR, and threat model

## Architecture

```text
Untrusted client
      |
      v
HTTP safeguards
      |
      v
JSON-RPC method allowlist
      |
      v
Strict tool arguments
      |
      v
Immutable public records
```

See [architecture](docs/architecture.md), [threat model](docs/threat-model.md), and [ADR 0001](docs/adr/0001-fixed-tool-surface.md).

## Run locally

```bash
cp .env.example .env
npm ci
npm test
npm run dev
```

Health check:

```bash
curl http://localhost:3000/health
```

List tools:

```bash
curl http://localhost:3000/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

Search projects:

```bash
curl http://localhost:3000/mcp \
  -H 'content-type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search_projects","arguments":{"query":"security","limit":3}}}'
```

## Security decisions

The server does not expose a generic query tool. Search runs in memory over known public fields. Tool schemas reject additional properties, and the server does not include a database or outbound HTTP adapter.

Pattern rejection is defense in depth. The main control is the absence of dangerous capabilities.

## Trade-offs

The in-memory rate limiter is appropriate for this standalone example, not for a multi-instance global quota. A production deployment should use an external atomic store and trust forwarded client identity only from a controlled edge proxy.

## License

MIT