npm package: @orchestraprime/rest-mcp-bridge
Description: Generic YAML-configuration-driven framework that converts any REST API into an MCP server
Author: OrchestraPrime LLC
Latest version: 1.0.0

# @orchestraprime/rest-mcp-bridge

**Convert any REST API into an MCP server with YAML configuration.**

A generic framework that lets you expose REST APIs as [Model Context Protocol](https://modelcontextprotocol.io/) servers for Claude, using declarative YAML configuration files and optional TypeScript escape hatches.

---

## Features

- **YAML-driven** -- Define tools, resources, and API connections in YAML
- **Zero boilerplate** -- 4-line entry point boots a full MCP server
- **4-level escape hatch system** -- Override any stage of the request pipeline with TypeScript
- **Built-in caching** -- TTL-based response caching with per-tool configuration
- **Schema generation** -- Auto-generates Zod + JSON Schema from YAML input definitions
- **Response transforms** -- JSONPath-lite expressions with pipe functions
- **Template engine** -- Mustache-like markdown formatting
- **Retry with backoff** -- Configurable exponential backoff for transient failures
- **Structured logging** -- SEC-2 compliant (no secrets logged)
- **API key validation** -- Pattern-based key validation at startup

---

## Quick Start

```typescript
import { loadConfig, createServer, connectStdio, loadEscapeHatches } from '@orchestraprime/rest-mcp-bridge';

const config = await loadConfig('./config');
await loadEscapeHatches(config);
const bridge = createServer(config);
await connectStdio(bridge);
```

---

## Configuration Structure

```
config/
  server.yaml           Server name, version, defaults
  apis.yaml             API connections (base_url, auth, retry)
  tools/
    my-tool.yaml        One file per MCP tool
  resources/
    my-resource.yaml    One file per MCP resource
```

### server.yaml

```yaml
name: my-mcp-server
version: 1.0.0
description: My MCP server
defaults:
  cache_ttl: 300
  timeout: 25000
  max_retries: 3
```

### apis.yaml

```yaml
my_api:
  base_url: https://api.example.com
  timeout: 25000
  auth:
    type: api-key
    header: x-api-key
    env_var: MY_API_KEY
    key_pattern: "^mk_[a-zA-Z0-9]{24,}$"
```

### Tool YAML

```yaml
name: search_items
description: Search for items by query
api: my_api
endpoint:
  method: POST
  path: /v1/search
input:
  properties:
    query:
      type: string
      description: Search query
    limit:
      type: integer
      description: Max results
      optional: true
      default: 10
      min: 1
      max: 50
  required:
    - query
response:
  results: "$.results"
  total: "$.metadata.total_count"
format:
  empty: "No results found for your query."
  header: "# Search Results ({{total}} found)\n\n"
  item: "- **{{title}}** ({{source}})\n  {{summary}}\n"
cache:
  ttl: 300
```

---

## Escape Hatch System

When YAML configuration isn't enough, use TypeScript escape hatches:

| Level | Hook | Use Case |
|-------|------|----------|
| L1 | `pre_request` | Inject local data, skip API call |
| L2 | `post_response` | Transform complex nested responses |
| L3 | `custom_formatter` | Status-dependent output formatting |
| L4 | `handler` | Full custom logic, no API call |

Reference escape hatches in your tool YAML:

```yaml
name: my_tool
pre_request: ./escape-hatches/my-tool-hook.ts
post_response: ./escape-hatches/my-tool-transform.ts
custom_formatter: ./escape-hatches/my-tool-format.ts
handler: ./escape-hatches/my-tool-handler.ts    # L4: replaces entire pipeline
```

### Escape Hatch Signatures

```typescript
import type {
  PreRequestHook,
  PostResponseTransform,
  CustomFormatter,
  FullHandler,
} from '@orchestraprime/rest-mcp-bridge';

// L1: Pre-request hook
export const preRequest: PreRequestHook = async (input, context) => {
  return { skip: true, localData: { /* ... */ } };
};

// L2: Post-response transform
export const postResponse: PostResponseTransform = async (raw) => {
  return { /* normalized data */ };
};

// L3: Custom formatter
export const customFormatter: CustomFormatter = async (data) => {
  return '# Formatted output\n...';
};

// L4: Full handler
export const handler: FullHandler = async (input, context) => {
  return { content: [{ type: 'text', text: '...' }] };
};
```

---

## Pipe Functions

Transform response values with built-in pipe functions:

```yaml
response:
  name: "$.name | uppercase"
  date: "$.created_at | date_format"
  count: "$.total | default:0"
  items: "$.results | take:5"
  source: "$.authority | lowercase"
```

Available pipes: `uppercase`, `lowercase`, `trim`, `truncate:N`, `default:value`, `date_format`, `round:N`, `take:N`, `join:separator`, `count`, `first`, `last`, `flatten`, `sort`, `unique`, and more.

---

## API

### Exports

```typescript
// Core
export { loadConfig } from './config-loader';
export { createServer, connectStdio } from './server-factory';
export { loadEscapeHatches } from './escape-hatch-loader';

// Types (for escape hatches)
export type {
  PreRequestHook, PostResponseTransform,
  CustomFormatter, FullHandler,
  ToolContext, McpToolResult,
  BridgeConfig, ToolConfig, ResourceConfig,
} from './types';

// Utilities (for escape hatches)
export { CacheManager } from './cache-manager';
export { withRetry } from './retry';
export { logger } from './logger';
export { BridgeError, ValidationError, /* ... */ } from './error-formatter';
```

---

## Testing

```bash
npm test              # 168 tests
npm test -- --coverage  # 97.5% statement coverage
```

---

## License

MIT -- see [LICENSE](./LICENSE) for details.