Skill: swgoettelman/deal-works (version 0.1.1)

--- name: deal-works description: AI agent infrastructure for deals, escrow, attestations, and autonomous agents. 39 tools across 9 engines. version: 0.1.1 author: swgoettelman license: MIT metadata: openclaw: emoji: "🤝" homepage: https://deal.works primaryEnv: DEAL_WORKS_API_KEY requires: env: - DEAL_WORKS_API_KEY bins: - node install: - kind: node package: "@swgoettelman/deal-works-mcp" bins: [deal-works-mcp] mcp: server: "@swgoettelman/deal-works-mcp" transport: stdio tags: - deals - escrow - smart-contracts - attestation - agents - blockchain - finance - mcp --- # deal.works AI agent infrastructure for trustworthy deal-making. Enables agents to create deals, manage escrow, deploy autonomous agents, and issue cryptographic attestations. ## Quick Start ```bash # Install the skill openclaw skill add swgoettelman/deal-works # Set your API key export DEAL_WORKS_API_KEY=your_key_here # The skill is now available to your agent ``` Get your API key at [hq.works/settings/api](https://hq.works/settings/api). ## Capabilities ### 9 Engines, 39 Tools | Engine | Tools | Purpose | |--------|-------|---------| | **Deal** | 7 | Deal lifecycle: create, sign, approve, search, timeline | | **Fund** | 6 | Wallets, transfers, escrow, cashout, agent funding | | **Bourse** | 5 | Marketplace for templates, skills, integrations | | **Cadre** | 6 | Deploy and manage autonomous agents | | **Oath** | 5 | Cryptographic attestations (completed on-chain) | | **Parler** | 4 | Dispute resolution and governance | | **Academy** | 3 | Learning platform for deal-making | | **HQ** | 2 | Dashboard metrics and system health | | **Clause** | 1 | Contract clause rendering | ### 7 Resources - `dealworks://profile` - User profile and trust tier - `dealworks://wallet` - Wallet balances - `dealworks://deals` - Active deals - `dealworks://agents` - Deployed agents - `dealworks://templates` - Available templates - `dealworks://disputes` - Open disputes - `dealworks://dashboard` - Key metrics ### 5 Pre-built Prompts - `escrow-deal` - Create escrow-protected deal workflow - `deploy-agent` - Deploy agent from skill - `file-dispute` - File dispute with evidence - `publish-template` - Publish template to Bourse - `portfolio-review` - Review deal portfolio ## Common Workflows ### Create an Escrow Deal ``` User: Create a $5000 escrow deal with [REDACTED_SECRET] for website development Agent: I'll use deal_create to create the deal, then fund_escrow to lock the funds, and oath_attest to prepare completion verification. ``` ### Deploy a Monitoring Agent ``` User: Deploy an agent to monitor my active deals and alert on SLA violations Agent: I'll use bourse_search to find monitoring skills, cadre_deploy to launch the agent, and fund_agent_fund to allocate operational budget. ``` ### File a Dispute ``` User: The contractor didn't deliver. File a dispute for deal xyz123. Agent: I'll use deal_get to review the deal, then parler_dispute_file with the evidence. The dispute enters the resolution queue. ``` ## Tool Reference ### Deal Engine | Tool | Description | |------|-------------| | `deal_list` | List deals with optional status filter | | `deal_create` | Create a new deal with terms and counterparty | | `deal_get` | Get detailed deal information | | `deal_action` | Perform action: SIGN, APPROVE, REJECT, CANCEL, COMPLETE, ARCHIVE | | `deal_search` | Search deals by text query | | `deal_timeline` | Get activity timeline for a deal | | `deal_attachments` | List deal attachments | ### Fund Engine | Tool | Description | |------|-------------| | `fund_balance` | Get wallet balance (available, locked, pending) | | `fund_transfer` | Transfer funds between wallets | | `fund_transactions` | List transaction history | | `fund_escrow` | Lock funds in escrow for a deal | | `fund_cashout` | Cash out to external wallet on Base | | `fund_agent_fund` | Fund an agent's operational wallet | ### Bourse Engine | Tool | Description | |------|-------------| | `bourse_search` | Search marketplace for templates/skills | | `bourse_get` | Get listing details and reviews | | `bourse_fork` | Fork a listing to customize | | `bourse_publish` | Publish to marketplace | | `bourse_earnings` | View earnings from published listings | ### Cadre Engine | Tool | Description | |------|-------------| | `cadre_list` | List deployed agents | | `cadre_deploy` | Deploy a new agent from skill | | `cadre_command` | Send command: START, STOP, RESTART, SCALE | | `cadre_health` | Get agent health status | | `cadre_delegations` | List permission delegations | | `cadre_sla_violations` | List SLA violations | ### Oath Engine | Tool | Description | |------|-------------| | `oath_attest` | Create attestation for deal milestone | | `oath_verify` | Verify attestation authenticity | | `oath_vault_upload` | Upload document hashes to vault | | `oath_vault_seal` | Seal vault with Merkle root on-chain | | `oath_trust_tier` | Get user/org trust tier | ### Parler Engine | Tool | Description | |------|-------------| | `parler_dispute_file` | File a dispute with evidence | | `parler_dispute_list` | List disputes by status/role | | `parler_proposals` | List resolution proposals | | `parler_vote` | Vote on proposal | ### Academy Engine | Tool | Description | |------|-------------| | `academy_courses` | Browse available courses | | `academy_enroll` | Enroll in a course | | `academy_tip` | Tip a course creator | ### HQ Engine | Tool | Description | |------|-------------| | `hq_dashboard` | Get dashboard metrics | | `hq_health` | Check system health | ### Clause Engine | Tool | Description | |------|-------------| | `clause_render` | Render contract clause template | ## Security - All mutations use idempotency keys (safe to retry) - API keys scoped per engine - Rate limited: 100 requests/minute - Circuit breaker per engine (5 failures = 30s cooldown) - Only calls *.works domains + approved external APIs ## Links - [deal.works](https://deal.works) - Main platform - [Documentation](https://docs.deal.works) - Full API docs - [npm package](https://www.npmjs.com/package/@swgoettelman/deal-works-mcp) - [GitHub](https://github.com/swgoettelman/titanium-federation)

path: README.md
content: # @swgoettelman/deal-works-mcp

MCP (Model Context Protocol) server exposing the deal.works platform to AI agents. Provides 39 tools across 9 engines for deal management, escrow, agent deployment, attestations, and more.

## Installation

```bash
npx @swgoettelman/deal-works-mcp
```

Or install globally:

```bash
npm install -g @swgoettelman/deal-works-mcp
```

## Configuration

### Environment Variables

```bash
export DEAL_WORKS_API_KEY=your_api_key_here
```

Get your API key from [hq.works/settings/api](https://hq.works/settings/api).

### Claude Desktop

Add to your `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "deal-works": {
      "command": "npx",
      "args": ["@goettelman/deal-works-mcp"],
      "env": {
        "DEAL_WORKS_API_KEY": "your_api_key_here"
      }
    }
  }
}
```

### Cursor / VS Code

Add to your MCP configuration:

```json
{
  "deal-works": {
    "command": "npx",
    "args": ["@goettelman/deal-works-mcp"],
    "env": {
      "DEAL_WORKS_API_KEY": "your_api_key_here"
    }
  }
}
```

### OpenClaw

```bash
openclaw skill add goettelman/deal-works
```

## Tools (39 total)

### Deal Engine (7 tools)
- `deal_list` - List deals with status filter
- `deal_create` - Create a new deal
- `deal_get` - Get deal details
- `deal_action` - Perform deal actions (sign, approve, reject, etc.)
- `deal_search` - Search deals by query
- `deal_timeline` - Get deal activity timeline
- `deal_attachments` - List deal attachments

### Fund Engine (6 tools)
- `fund_balance` - Get wallet balance
- `fund_transfer` - Transfer funds between wallets
- `fund_transactions` - List transaction history
- `fund_escrow` - Lock funds in escrow for a deal
- `fund_cashout` - Cash out to external wallet
- `fund_agent_fund` - Fund an agent's operational wallet

### Bourse Engine (5 tools)
- `bourse_search` - Search marketplace for templates/skills
- `bourse_get` - Get listing details
- `bourse_fork` - Fork a listing
- `bourse_publish` - Publish to marketplace
- `bourse_earnings` - View earnings from published listings

### Cadre Engine (6 tools)
- `cadre_list` - List deployed agents
- `cadre_deploy` - Deploy a new agent
- `cadre_command` - Send command to agent (start/stop/restart)
- `cadre_health` - Get agent health status
- `cadre_delegations` - List permission delegations
- `cadre_sla_violations` - List SLA violations

### Oath Engine (5 tools)
- `oath_attest` - Create attestation for a deal
- `oath_verify` - Verify attestation authenticity
- `oath_vault_upload` - Upload documents to secure vault
- `oath_vault_seal` - Seal vault with Merkle root
- `oath_trust_tier` - Get user/org trust tier

### Parler Engine (4 tools)
- `parler_dispute_file` - File a dispute
- `parler_dispute_list` - List disputes
- `parler_proposals` - List resolution proposals
- `parler_vote` - Vote on proposals

### Academy Engine (3 tools)
- `academy_courses` - Browse courses
- `academy_enroll` - Enroll in a course
- `academy_tip` - Tip a course creator

### HQ Engine (2 tools)
- `hq_dashboard` - Get dashboard metrics
- `hq_health` - Check system health

### Clause Engine (1 tool)
- `clause_render` - Render contract clause template

## Resources (7 total)

- `dealworks://profile` - User profile and trust tier
- `dealworks://wallet` - Wallet balances
- `dealworks://deals` - Active deals
- `dealworks://agents` - Deployed agents
- `dealworks://templates` - Available templates
- `dealworks://disputes` - Open disputes
- `dealworks://dashboard` - Key metrics

## Prompts (5 total)

- `escrow-deal` - Create escrow-protected deal workflow
- `deploy-agent` - Deploy agent from skill
- `file-dispute` - File dispute with evidence
- `publish-template` - Publish template to Bourse
- `portfolio-review` - Review deal portfolio

## Example Usage

### Create an Escrow Deal

```
User: Create a $5000 escrow deal with [REDACTED_SECRET] for website development

Claude: I'll help you create an escrow-protected deal. Let me:
1. Create the deal
2. Set up escrow
3. Prepare attestation for completion
```

### Deploy an Agent

```
User: Deploy a monitoring agent with a $100 budget

Claude: I'll search for monitoring skills and deploy an agent:
1. Searching Bourse for monitoring skills...
2. Deploying agent with SLA configuration...
3. Funding agent wallet...
```

## Security

- API keys are scoped per engine
- All mutations use idempotency keys
- Rate limited to 100 requests/minute
- Circuit breaker per engine (5 failures = 30s cooldown)
- Only calls *.works domains + approved external APIs

## License

MIT

## Links

- [deal.works](https://deal.works)
- [Documentation](https://docs.deal.works)
- [API Reference](https://docs.deal.works/api)
- [GitHub](https://github.com/goettelman/deal.works)


path: configs/claude-desktop.json
content: {
  "mcpServers": {
    "deal-works": {
      "command": "npx",
      "args": ["@swgoettelman/deal-works-mcp"],
      "env": {
        "DEAL_WORKS_API_KEY": "your_api_key_here"
      }
    }
  }
}


path: configs/cursor.json
content: {
  "deal-works": {
    "command": "npx",
    "args": ["@swgoettelman/deal-works-mcp"],
    "env": {
      "DEAL_WORKS_API_KEY": "your_api_key_here"
    }
  }
}


path: configs/perplexity-prompts.md
content: # deal.works Perplexity Computer Prompts

Pre-built prompts for common multi-agent workflows using deal.works MCP tools.

## Setup

1. Install the deal.works MCP connector in Perplexity Mac app
2. Set your `DEAL_WORKS_API_KEY` environment variable
3. Copy any prompt below and paste into Perplexity

---

## Prompt 1: Escrow Deal Creation

**Use case**: Create a protected deal with automatic escrow

```
I need to create an escrow-protected deal. Here are the details:

- Counterparty: [EMAIL]
- Amount: $[AMOUNT]
- Description: [WHAT THE DEAL IS FOR]
- Due date: [DATE]

Please:
1. Use deal_create to create the deal with these terms
2. Use fund_balance to check I have sufficient funds
3. Use fund_escrow to lock the amount for this deal
4. Use oath_attest to prepare a completion attestation template
5. Summarize the deal ID, escrow status, and next steps

Confirm each step before proceeding.
```

**Expected token usage**: ~3,000-4,000 tokens

---

## Prompt 2: Deploy Monitoring Agent

**Use case**: Deploy an autonomous agent to monitor deals and alert on issues

```
Deploy a monitoring agent for my deal.works account:

- Budget: [AMOUNT] Talers
- Monitor: Active deals, SLA violations, escrow status
- Alert on: Late deliveries, low balance, agent failures

Steps:
1. Use bourse_search to find monitoring/alerting skills
2. Show me the top 3 options with quality scores
3. After I choose, use cadre_deploy to launch the agent
4. Use fund_agent_fund to allocate the budget
5. Use cadre_health to verify the agent is running
6. Show me the agent ID and how to check its status later
```

**Expected token usage**: ~4,000-5,000 tokens

---

## Prompt 3: Portfolio Review

**Use case**: Comprehensive review of your deal.works portfolio

```
Give me a complete portfolio review:

1. Use deal_list to show all active deals with status
2. Use fund_balance to show wallet balances (available, locked, pending)
3. Use cadre_list to show deployed agents and their health
4. Use cadre_sla_violations to check for any SLA issues
5. Use hq_dashboard to get key metrics for this week

Summarize:
- Total deal volume and value
- Funds in escrow vs available
- Agent performance metrics
- Any items needing immediate attention

Format as a structured report.
```

**Expected token usage**: ~5,000-6,000 tokens

---

## Prompt 4: Dispute Resolution

**Use case**: File and manage a dispute for a problematic deal

```
I need to file a dispute for deal [DEAL_ID]:

Reason: [NON_DELIVERY / QUALITY_ISSUE / LATE_DELIVERY / PAYMENT_DISPUTE]
Description: [DETAILED DESCRIPTION OF THE ISSUE]

Please:
1. Use deal_get to retrieve the deal details
2. Use deal_timeline to show the activity history
3. Use oath_verify to check any existing attestations
4. Use parler_dispute_file to file the dispute with my description
5. Use parler_dispute_list to confirm the dispute is registered
6. Explain the resolution process and timeline
```

**Expected token usage**: ~4,000-5,000 tokens

---

## Prompt 5: Marketplace Publishing

**Use case**: Publish a deal template to earn passive income

```
I want to publish a deal template to the Bourse marketplace:

Template name: [NAME]
Category: [freelance / consulting / real-estate / saas / other]
Description: [WHAT THIS TEMPLATE IS FOR]
Price: [AMOUNT] Talers (0 for free)

Steps:
1. Use bourse_search to check for similar templates
2. Show me competing templates and their prices
3. Based on the market, recommend a price point
4. Use bourse_publish to create the listing
5. Use bourse_get to confirm it's live
6. Explain how I'll earn from forks and usage
```

**Expected token usage**: ~3,000-4,000 tokens

---

## Multi-Step Workflow: Complete Deal Lifecycle

**Use case**: End-to-end deal from creation to completion

```
Guide me through a complete deal lifecycle:

Deal: [DESCRIPTION]
Counterparty: [EMAIL]
Amount: $[AMOUNT]
Deliverable: [WHAT WILL BE DELIVERED]

Phase 1 - Setup:
1. deal_create with terms
2. fund_escrow to lock funds
3. Share deal link with counterparty

Phase 2 - Execution (after counterparty signs):
4. deal_action SIGN to co-sign
5. oath_vault_upload for any documents
6. cadre_deploy a reminder agent (optional)

Phase 3 - Completion:
7. Verify deliverable received
8. oath_attest COMPLETION with evidence
9. deal_action COMPLETE to release escrow
10. fund_transactions to confirm payment

Track progress and prompt me at each phase.
```

**Expected token usage**: ~6,000-8,000 tokens (full lifecycle)

---

## Token Consumption Notes

| Workflow | Est. Tokens | Sonar Model |
|----------|-------------|-------------|
| Simple query | 1,000-2,000 | sonar |
| Escrow deal | 3,000-4,000 | sonar-pro |
| Agent deploy | 4,000-5,000 | sonar-pro |
| Portfolio review | 5,000-6,000 | sonar-pro |
| Dispute filing | 4,000-5,000 | sonar-pro |
| Full lifecycle | 6,000-8,000 | sonar-reasoning-pro |

Perplexity Max subscribers: Unlimited queries. Pro subscribers: Check your quota at perplexity.ai/settings.


path: configs/perplexity.json
content: {
  "$schema": "https://perplexity.ai/schemas/mcp-connector.json",
  "name": "deal.works",
  "description": "AI agent infrastructure for deals, escrow, attestations, and autonomous agents",
  "command": "npx",
  "args": ["@swgoettelman/deal-works-mcp"],
  "env": {
    "DEAL_WORKS_API_KEY": "${DEAL_WORKS_API_KEY}"
  },
  "icon": "https://deal.works/favicon.ico",
  "homepage": "https://deal.works",
  "documentation": "https://docs.deal.works/mcp"
}


path: configs/vscode.json
content: {
  "mcp.servers": {
    "deal-works": {
      "command": "npx",
      "args": ["@swgoettelman/deal-works-mcp"],
      "env": {
        "DEAL_WORKS_API_KEY": "your_api_key_here"
      }
    }
  }
}


path: dist/index.d.ts
content: 
export {  }


path: package.json
content: {
  "name": "@swgoettelman/deal-works-mcp",
  "version": "0.1.1",
  "description": "MCP server exposing deal.works platform tools to AI agents - 39 tools across 9 engines for deals, escrow, agents, attestations, and more",
  "type": "module",
  "main": "./dist/index.js",
  "module": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "bin": {
    "deal-works-mcp": "./dist/index.js"
  },
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "types": "./dist/index.d.ts"
    }
  },
  "scripts": {
    "build": "tsup",
    "dev": "tsup --watch",
    "typecheck": "tsc --noEmit",
    "lint": "eslint src/",
    "test": "vitest run",
    "test:watch": "vitest",
    "prepublishOnly": "pnpm build"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.25.3",
    "zod": "^3.23.8"
  },
  "devDependencies": {
    "@goettelman/config": "workspace:*",
    "@types/node": "^22.9.0",
    "eslint": "^9.15.0",
    "tsup": "^8.3.5",
    "typescript": "^5.7.2",
    "typescript-eslint": "^8.15.0",
    "vitest": "^2.1.0"
  },
  "files": [
    "dist",
    "configs",
    "README.md",
    "SKILL.md",
    "LICENSE"
  ],
  "keywords": [
    "mcp",
    "model-context-protocol",
    "deal-works",
    "ai-agents",
    "escrow",
    "smart-contracts",
    "attestation",
    "claude",
    "openai",
    "anthropic",
    "perplexity",
    "openclaw"
  ],
  "license": "MIT",
  "repository": {
    "type": "git",
    "url": "https://github.com/goettelman/deal.works.git",
    "directory": "packages/deal-works-mcp"
  },
  "homepage": "https://deal.works",
  "bugs": {
    "url": "https://github.com/goettelman/deal.works/issues"
  },
  "publishConfig": {
    "access": "public"
  },
  "engines": {
    "node": ">=18.0.0"
  }
}


path: src/auth.ts
content: /**
 * Authentication handling for MCP server
 * Supports Bearer tokens and OAuth2 PKCE flow
 */

import { z } from "zod";

export const AuthConfigSchema = z.object({
  [REDACTED_SECRET]).optional(),
  oauth2: z.object({
    clientId: z.string(),
    redirectUri: z.string(),
    scopes: z.array(z.string()).default([]),
  }).optional(),
});

export type AuthConfig = z.infer<typeof AuthConfigSchema>;

export interface AuthContext {
  apiKey?: string;
  accessToken?: string;
  userId?: string;
  orgId?: string;
  scopes: string[];
}

/**
 * Extract auth context from environment or request headers
 */
export function getAuthContext(headers?: Record<string, string>): AuthContext {
  const [REDACTED_SECRET] ?? headers?.["x-api-key"];
  const accessToken = headers?.["authorization"]?.replace(/^Bearer\s+/i, "");

  return {
    apiKey,
    accessToken: accessToken ?? apiKey,
    userId: headers?.["x-user-id"],
    orgId: headers?.["x-org-id"],
    scopes: parseScopes(headers?.["x-scopes"]),
  };
}

function parseScopes(scopeHeader?: string): string[] {
  if (!scopeHeader) return [];
  return scopeHeader.split(/[\s,]+/).filter(Boolean);
}

/**
 * Validate that auth context has required scopes
 */
export function requireScopes(context: AuthContext, required: string[]): void {
  if (required.length === 0) return;

  // API key has all scopes
  if (context.apiKey) return;

  const missing = required.filter((s) => !context.scopes.includes(s));
  if (missing.length > 0) {
    throw new Error(`Missing required scopes: ${missing.join(", ")}`);
  }
}

/**
 * Generate OAuth2 PKCE authorization URL
 */
export function generateAuthUrl(
  config: NonNullable<AuthConfig["oauth2"]>,
  state: string,
  codeVerifier: string
): string {
  const codeChallenge = generateCodeChallenge(codeVerifier);

  const params = new URLSearchParams({
    client_id: config.clientId,
    redirect_uri: config.redirectUri,
    response_type: "code",
    scope: config.scopes.join(" "),
    state,
    code_challenge: codeChallenge,
    code_challenge_method: "S256",
  });

  return `https://hq.works/oauth/authorize?${params.toString()}`;
}

/**
 * Generate PKCE code challenge from verifier
 */
function generateCodeChallenge(verifier: string): string {
  // In Node.js, use crypto for SHA256
  const crypto = globalThis.crypto ?? require("crypto");
  const encoder = new TextEncoder();
  const data = encoder.encode(verifier);

  // Synchronous hash for simplicity - in production use async
  const hash = crypto.createHash?.("sha256").update(verifier).digest()
    ?? new Uint8Array(32); // Fallback for browser

  return base64UrlEncode(hash);
}

function base64UrlEncode(buffer: ArrayBuffer | Uint8Array): string {
  const bytes = buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer);
  let binary = "";
  for (const byte of bytes) {
    binary += String.fromCharCode(byte);
  }
  return btoa(binary)
    .replace(/\+/g, "-")
    .replace(/\//g, "_")
    .replace(/=+$/, "");
}

/**
 * Generate random code verifier for PKCE
 */
export function generateCodeVerifier(): string {
  const array = new Uint8Array(32);
  globalThis.crypto?.getRandomValues?.(array) ??
    require("crypto").randomFillSync(array);
  return base64UrlEncode(array);
}


path: src/client.ts
content: /**
 * Shared HTTP client for engine API calls
 * Handles authentication, retries, and circuit breaking
 */

import { z } from "zod";

export interface EngineClientConfig {
  apiKey?: string;
  baseUrls?: Record<string, string>;
  timeout?: number;
  maxRetries?: number;
}

export interface EngineResponse<T = unknown> {
  success: boolean;
  data?: T;
  error?: {
    code: string;
    message: string;
    retryable: boolean;
  };
}

const DEFAULT_BASE_URLS: Record<string, string> = {
  deal: "https://deal.works/api",
  fund: "https://fund.works/api",
  bourse: "https://bourse.works/api",
  cadre: "https://cadre.works/api",
  oath: "https://oath.works/api",
  parler: "https://parler.works/api",
  academy: "https://academy.works/api",
  hq: "https://hq.works/api",
  clause: "https://clause.works/api",
};

const RETRYABLE_STATUS_CODES = [429, 502, 503, 504];
const RETRY_DELAYS = [1000, 2000, 4000];

// Circuit breaker state per engine
const circuitState: Record<string, {
  failures: number;
  lastFailure: number;
  openUntil: number;
}> = {};

const CIRCUIT_FAILURE_THRESHOLD = 5;
const CIRCUIT_WINDOW_MS = 60000;
const CIRCUIT_OPEN_DURATION_MS = 30000;

function checkCircuit(engine: string): void {
  const state = circuitState[engine];
  if (!state) return;

  const now = Date.now();
  if (state.openUntil > now) {
    throw new Error(`Circuit open for ${engine} engine. Retry after ${Math.ceil((state.openUntil - now) / 1000)}s`);
  }

  // Reset if window expired
  if (now - state.lastFailure > CIRCUIT_WINDOW_MS) {
    state.failures = 0;
  }
}

function recordFailure(engine: string): void {
  if (!circuitState[engine]) {
    circuitState[engine] = { failures: 0, lastFailure: 0, openUntil: 0 };
  }

  const state = circuitState[engine];
  const now = Date.now();

  state.failures++;
  state.lastFailure = now;

  if (state.failures >= CIRCUIT_FAILURE_THRESHOLD) {
    state.openUntil = now + CIRCUIT_OPEN_DURATION_MS;
    state.failures = 0;
  }
}

function recordSuccess(engine: string): void {
  if (circuitState[engine]) {
    circuitState[engine].failures = 0;
  }
}

export class EngineClient {
  private readonly [REDACTED_SECRET] | undefined;
  private readonly baseUrls: Record<string, string>;
  private readonly timeout: number;
  private readonly maxRetries: number;

  constructor(config: EngineClientConfig = {}) {
    this.[REDACTED_SECRET] ?? process.env.DEAL_WORKS_API_KEY;
    this.baseUrls = { ...DEFAULT_BASE_URLS, ...config.baseUrls };
    this.timeout = config.timeout ?? 30000;
    this.maxRetries = config.maxRetries ?? 3;
  }

  async fetch<T>(
    engine: string,
    path: string,
    options: {
      method?: "GET" | "POST" | "PUT" | "DELETE";
      body?: unknown;
      idempotencyKey?: string;
    } = {}
  ): Promise<EngineResponse<T>> {
    const { method = "GET", body, idempotencyKey } = options;

    checkCircuit(engine);

    const baseUrl = this.baseUrls[engine];
    if (!baseUrl) {
      return {
        success: false,
        error: {
          code: "UNKNOWN_ENGINE",
          message: `Unknown engine: ${engine}`,
          retryable: false,
        },
      };
    }

    const url = `${baseUrl}${path}`;
    const headers: Record<string, string> = {
      "Content-Type": "application/json",
      "User-Agent": "@goettelman/deal-works-mcp/0.1.0",
    };

    if (this.apiKey) {
      headers["Authorization"] = `Bearer ${this.apiKey}`;
    }

    if (idempotencyKey) {
      headers["X-Idempotency-Key"] = idempotencyKey;
    }

    let lastError: Error | null = null;

    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        const controller = new AbortController();
        const timeoutId = setTimeout(() => controller.abort(), this.timeout);

        const response = await fetch(url, {
          method,
          headers,
          body: body ? JSON.stringify(body) : undefined,
          signal: controller.signal,
        });

        clearTimeout(timeoutId);

        if (response.ok) {
          recordSuccess(engine);
          const data = await response.json() as T;
          return { success: true, data };
        }

        const isRetryable = RETRYABLE_STATUS_CODES.includes(response.status);

        if (!isRetryable || attempt === this.maxRetries) {
          recordFailure(engine);
          const errorBody = await response.text();
          return {
            success: false,
            error: {
              code: `HTTP_${response.status}`,
              message: errorBody || response.statusText,
              retryable: isRetryable,
            },
          };
        }

        // Wait before retry
        await sleep(RETRY_DELAYS[attempt] ?? 4000);
      } catch (err) {
        lastError = err instanceof Error ? err : new Error(String(err));

        if (attempt === this.maxRetries) {
          recordFailure(engine);
          return {
            success: false,
            error: {
              code: "NETWORK_ERROR",
              message: lastError.message,
              retryable: true,
            },
          };
        }

        await sleep(RETRY_DELAYS[attempt] ?? 4000);
      }
    }

    return {
      success: false,
      error: {
        code: "MAX_RETRIES_EXCEEDED",
        message: lastError?.message ?? "Max retries exceeded",
        retryable: false,
      },
    };
  }
}

function sleep(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

// Singleton instance
let clientInstance: EngineClient | null = null;

export function getEngineClient(config?: EngineClientConfig): EngineClient {
  if (!clientInstance || config) {
    clientInstance = new EngineClient(config);
  }
  return clientInstance;
}


path: src/engines/academy.ts
content: /**
 * Academy Engine - 3 tools
 * Learning platform for deal-making skills
 */

import { z } from "zod";
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
import { getEngineClient } from "../client.js";

// Input schemas
const AcademyCoursesInputSchema = z.object({
  category: z.enum(["DEALS", "FINANCE", "LEGAL", "AGENTS", "BLOCKCHAIN"]).optional(),
  level: z.enum(["BEGINNER", "INTERMEDIATE", "ADVANCED"]).optional(),
  limit: z.number().min(1).max(100).default(20),
});

const AcademyEnrollInputSchema = z.object({
  courseId: z.string().min(1),
});

const AcademyTipInputSchema = z.object({
  courseId: z.string().min(1),
  amount: z.number().positive(),
  message: z.string().max(500).optional(),
});

// Tool definitions
export const academyTools: Tool[] = [
  {
    name: "academy_courses",
    description: "Browse available courses on the Academy platform.",
    inputSchema: {
      type: "object",
      properties: {
        category: {
          type: "string",
          enum: ["DEALS", "FINANCE", "LEGAL", "AGENTS", "BLOCKCHAIN"],
          description: "Filter by category",
        },
        level: {
          type: "string",
          enum: ["BEGINNER", "INTERMEDIATE", "ADVANCED"],
          description: "Filter by difficulty level",
        },
        limit: {
          type: "number",
          description: "Max courses to return",
          default: 20,
        },
      },
    },
  },
  {
    name: "academy_enroll",
    description: "Enroll in a course. Some courses may require payment.",
    inputSchema: {
      type: "object",
      properties: {
        courseId: {
          type: "string",
          description: "Course ID to enroll in",
        },
      },
      required: ["courseId"],
    },
  },
  {
    name: "academy_tip",
    description: "Tip a course creator to show appreciation.",
    inputSchema: {
      type: "object",
      properties: {
        courseId: {
          type: "string",
          description: "Course ID",
        },
        amount: {
          type: "number",
          description: "Tip amount in Talers",
        },
        message: {
          type: "string",
          description: "Optional thank-you message",
        },
      },
      required: ["courseId", "amount"],
    },
  },
];

// Tool handlers
export async function handleAcademyTool(
  name: string,
  args: Record<string, unknown>
): Promise<unknown> {
  const client = getEngineClient();

  switch (name) {
    case "academy_courses": {
      const input = AcademyCoursesInputSchema.parse(args);
      const params = new URLSearchParams();
      if (input.category) params.set("category", input.category);
      if (input.level) params.set("level", input.level);
      params.set("limit", String(input.limit));
      return client.fetch("academy", `/courses?${params.toString()}`);
    }

    case "academy_enroll": {
      const input = AcademyEnrollInputSchema.parse(args);
      return client.fetch("academy", `/courses/${input.courseId}/enroll`, {
        method: "POST",
        idempotencyKey: `enroll-${input.courseId}-${Date.now()}`,
      });
    }

    case "academy_tip": {
      const input = AcademyTipInputSchema.parse(args);
      return client.fetch("academy", `/courses/${input.courseId}/tip`, {
        method: "POST",
        body: { amount: input.amount, message: input.message },
        idempotencyKey: `tip-${input.courseId}-${Date.now()}`,
      });
    }

    default:
      throw new Error(`Unknown academy tool: ${name}`);
  }
}


path: src/engines/bourse.ts
content: /**
 * Bourse Engine - 5 tools
 * Marketplace for deal templates, skills, and integrations
 */

import { z } from "zod";
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
import { getEngineClient } from "../client.js";

// Input schemas
const BourseSearchInputSchema = z.object({
  query: z.string().min(1).max(200),
  category: z.enum(["TEMPLATE", "SKILL", "INTEGRATION", "CONNECTOR"]).optional(),
  minQuality: z.number().min(0).max(100).optional(),
  source: z.enum(["OFFICIAL", "COMMUNITY", "ACTIVEPIECES"]).optional(),
  limit: z.number().min(1).max(100).default(20),
});

const BourseGetInputSchema = z.object({
  listingId: z.string().min(1),
});

const BourseForkInputSchema = z.object({
  listingId: z.string().min(1),
  newName: z.string().min(1).max(100).optional(),
});

const BoursePublishInputSchema = z.object({
  type: z.enum(["TEMPLATE", "SKILL", "INTEGRATION"]),
  name: z.string().min(1).max(100),
  description: z.string().max(2000),
  content: z.record(z.unknown()),
  price: z.number().min(0).default(0),
  tags: z.array(z.string()).default([]),
});

const BourseEarningsInputSchema = z.object({
  period: z.enum(["DAY", "WEEK", "MONTH", "ALL"]).default("MONTH"),
});

// Tool definitions
export const bourseTools: Tool[] = [
  {
    name: "bourse_search",
    description: "Search the Bourse marketplace for templates, skills, and integrations.",
    inputSchema: {
      type: "object",
      properties: {
        query: {
          type: "string",
          description: "Search query",
        },
        category: {
          type: "string",
          enum: ["TEMPLATE", "SKILL", "INTEGRATION", "CONNECTOR"],
          description: "Filter by category",
        },
        minQuality: {
          type: "number",
          description: "Minimum quality score (0-100)",
        },
        source: {
          type: "string",
          enum: ["OFFICIAL", "COMMUNITY", "ACTIVEPIECES"],
          description: "Filter by source",
        },
        limit: {
          type: "number",
          description: "Max results",
          default: 20,
        },
      },
      required: ["query"],
    },
  },
  {
    name: "bourse_get",
    description: "Get detailed information about a Bourse listing including reviews and usage stats.",
    inputSchema: {
      type: "object",
      properties: {
        listingId: {
          type: "string",
          description: "Listing ID",
        },
      },
      required: ["listingId"],
    },
  },
  {
    name: "bourse_fork",
    description: "Fork a Bourse listing to create your own customized version.",
    inputSchema: {
      type: "object",
      properties: {
        listingId: {
          type: "string",
          description: "Listing ID to fork",
        },
        newName: {
          type: "string",
          description: "Name for your forked version",
        },
      },
      required: ["listingId"],
    },
  },
  {
    name: "bourse_publish",
    description: "Publish a new template, skill, or integration to the Bourse marketplace.",
    inputSchema: {
      type: "object",
      properties: {
        type: {
          type: "string",
          enum: ["TEMPLATE", "SKILL", "INTEGRATION"],
          description: "Listing type",
        },
        name: {
          type: "string",
          description: "Listing name",
        },
        description: {
          type: "string",
          description: "Description",
        },
        content: {
          type: "object",
          description: "Listing content/definition",
        },
        price: {
          type: "number",
          description: "Price in Talers (0 for free)",
          default: 0,
        },
        tags: {
          type: "array",
          items: { type: "string" },
          description: "Tags for discoverability",
        },
      },
      required: ["type", "name", "description", "content"],
    },
  },
  {
    name: "bourse_earnings",
    description: "View your Bourse earnings from published listings.",
    inputSchema: {
      type: "object",
      properties: {
        period: {
          type: "string",
          enum: ["DAY", "WEEK", "MONTH", "ALL"],
          description: "Time period",
          default: "MONTH",
        },
      },
    },
  },
];

// Tool handlers
export async function handleBourseTool(
  name: string,
  args: Record<string, unknown>
): Promise<unknown> {
  const client = getEngineClient();

  switch (name) {
    case "bourse_search": {
      const input = BourseSearchInputSchema.parse(args);
      return client.fetch("bourse", "/listings/search", {
        method: "POST",
        body: input,
      });
    }

    case "bourse_get": {
      const input = BourseGetInputSchema.parse(args);
      return client.fetch("bourse", `/listings/${input.listingId}`);
    }

    case "bourse_fork": {
      const input = BourseForkInputSchema.parse(args);
      return client.fetch("bourse", `/listings/${input.listingId}/fork`, {
        method: "POST",
        body: { newName: input.newName },
        idempotencyKey: `fork-${input.listingId}-${Date.now()}`,
      });
    }

    case "bourse_publish": {
      const input = BoursePublishInputSchema.parse(args);
      return client.fetch("bourse", "/listings", {
        method: "POST",
        body: input,
        idempotencyKey: `publish-${Date.now()}`,
      });
    }

    case "bourse_earnings": {
      const input = BourseEarningsInputSchema.parse(args);
      return client.fetch("bourse", `/earnings?period=${input.period}`);
    }

    default:
      throw new Error(`Unknown bourse tool: ${name}`);
  }
}


path: src/engines/cadre.ts
content: /**
 * Cadre Engine - 6 tools
 * Agent deployment, management, and monitoring
 */

import { z } from "zod";
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
import { getEngineClient } from "../client.js";

// Input schemas
const CadreListInputSchema = z.object({
  status: z.enum(["RUNNING", "STOPPED", "FAILED", "PENDING"]).optional(),
  limit: z.number().min(1).max(100).default(20),
  offset: z.number().min(0).default(0),
});

const CadreDeployInputSchema = z.object({
  name: z.string().min(1).max(100),
  skillId: z.string().min(1),
  config: z.record(z.unknown()).optional(),
  fundingAmount: z.number().positive().optional(),
  slaConfig: z.object({
    maxLatencyMs: z.number().positive().optional(),
    minUptime: z.number().min(0).max(100).optional(),
  }).optional(),
});

const CadreCommandInputSchema = z.object({
  agentId: z.string().min(1),
  command: z.enum(["START", "STOP", "RESTART", "SCALE_UP", "SCALE_DOWN"]),
  params: z.record(z.unknown()).optional(),
});

const CadreHealthInputSchema = z.object({
  agentId: z.string().min(1),
});

const CadreDelegationsInputSchema = z.object({
  agentId: z.string().optional(),
  status: z.enum(["ACTIVE", "REVOKED", "EXPIRED"]).optional(),
});

const CadreSlaViolationsInputSchema = z.object({
  agentId: z.string().optional(),
  severity: z.enum(["LOW", "MEDIUM", "HIGH", "CRITICAL"]).optional(),
  fromDate: z.string().datetime().optional(),
  limit: z.number().min(1).max(100).default(50),
});

// Tool definitions
export const cadreTools: Tool[] = [
  {
    name: "cadre_list",
    description: "List deployed agents with optional status filter.",
    inputSchema: {
      type: "object",
      properties: {
        status: {
          type: "string",
          enum: ["RUNNING", "STOPPED", "FAILED", "PENDING"],
          description: "Filter by agent status",
        },
        limit: {
          type: "number",
          description: "Max agents to return",
          default: 20,
        },
        offset: {
          type: "number",
          description: "Pagination offset",
          default: 0,
        },
      },
    },
  },
  {
    name: "cadre_deploy",
    description: "Deploy a new agent from a skill definition. Optionally fund and configure SLA.",
    inputSchema: {
      type: "object",
      properties: {
        name: {
          type: "string",
          description: "Agent name",
        },
        skillId: {
          type: "string",
          description: "Skill ID from Bourse to deploy",
        },
        config: {
          type: "object",
          description: "Agent configuration overrides",
        },
        fundingAmount: {
          type: "number",
          description: "Initial funding amount in Talers",
        },
        slaConfig: {
          type: "object",
          properties: {
            maxLatencyMs: { type: "number" },
            minUptime: { type: "number" },
          },
          description: "SLA configuration",
        },
      },
      required: ["name", "skillId"],
    },
  },
  {
    name: "cadre_command",
    description: "Send a command to an agent: start, stop, restart, or scale.",
    inputSchema: {
      type: "object",
      properties: {
        agentId: {
          type: "string",
          description: "Agent ID",
        },
        command: {
          type: "string",
          enum: ["START", "STOP", "RESTART", "SCALE_UP", "SCALE_DOWN"],
          description: "Command to execute",
        },
        params: {
          type: "object",
          description: "Command parameters (e.g., scale factor)",
        },
      },
      required: ["agentId", "command"],
    },
  },
  {
    name: "cadre_health",
    description: "Get health status and metrics for a specific agent.",
    inputSchema: {
      type: "object",
      properties: {
        agentId: {
          type: "string",
          description: "Agent ID",
        },
      },
      required: ["agentId"],
    },
  },
  {
    name: "cadre_delegations",
    description: "List permission delegations granted to agents.",
    inputSchema: {
      type: "object",
      properties: {
        agentId: {
          type: "string",
          description: "Filter by agent ID",
        },
        status: {
          type: "string",
          enum: ["ACTIVE", "REVOKED", "EXPIRED"],
          description: "Filter by delegation status",
        },
      },
    },
  },
  {
    name: "cadre_sla_violations",
    description: "List SLA violations for agents. Used for monitoring and alerting.",
    inputSchema: {
      type: "object",
      properties: {
        agentId: {
          type: "string",
          description: "Filter by agent ID",
        },
        severity: {
          type: "string",
          enum: ["LOW", "MEDIUM", "HIGH", "CRITICAL"],
          description: "Filter by severity",
        },
        fromDate: {
          type: "string",
          description: "Start date filter (ISO 8601)",
        },
        limit: {
          type: "number",
          description: "Max violations to return",
          default: 50,
        },
      },
    },
  },
];

// Tool handlers
export async function handleCadreTool(
  name: string,
  args: Record<string, unknown>
): Promise<unknown> {
  const client = getEngineClient();

  switch (name) {
    case "cadre_list": {
      const input = CadreListInputSchema.parse(args);
      const params = new URLSearchParams();
      if (input.status) params.set("status", input.status);
      params.set("limit", String(input.limit));
      params.set("offset", String(input.offset));
      return client.fetch("cadre", `/agents?${params.toString()}`);
    }

    case "cadre_deploy": {
      const input = CadreDeployInputSchema.parse(args);
      return client.fetch("cadre", "/agents", {
        method: "POST",
        body: input,
        idempotencyKey: `deploy-${input.skillId}-${Date.now()}`,
      });
    }

    case "cadre_command": {
      const input = CadreCommandInputSchema.parse(args);
      return client.fetch("cadre", `/agents/${input.agentId}/command`, {
        method: "POST",
        body: { command: input.command, params: input.params },
        idempotencyKey: `command-${input.agentId}-${input.command}-${Date.now()}`,
      });
    }

    case "cadre_health": {
      const input = CadreHealthInputSchema.parse(args);
      return client.fetch("cadre", `/agents/${input.agentId}/health`);
    }

    case "cadre_delegations": {
      const input = CadreDelegationsInputSchema.parse(args);
      const params = new URLSearchParams();
      if (input.agentId) params.set("agentId", input.agentId);
      if (input.status) params.set("status", input.status);
      return client.fetch("cadre", `/delegations?${params.toString()}`);
    }

    case "cadre_sla_violations": {
      const input = CadreSlaViolationsInputSchema.parse(args);
      const params = new URLSearchParams();
      if (input.agentId) params.set("agentId", input.agentId);
      if (input.severity) params.set("severity", input.severity);
      if (input.fromDate) params.set("fromDate", input.fromDate);
      params.set("limit", String(input.limit));
      return client.fetch("cadre", `/sla-violations?${params.toString()}`);
    }

    default:
      throw new Error(`Unknown cadre tool: ${name}`);
  }
}


path: src/engines/clause.ts
content: /**
 * Clause Engine - 1 tool
 * Contract clause rendering and templating
 */

import { z } from "zod";
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
import { getEngineClient } from "../client.js";

// Input schemas
const ClauseRenderInputSchema = z.object({
  templateId: z.string().min(1),
  variables: z.record(z.union([z.string(), z.number(), z.boolean()])),
  format: z.enum(["HTML", "MARKDOWN", "PLAIN"]).default("MARKDOWN"),
});

// Tool definitions
export const clauseTools: Tool[] = [
  {
    name: "clause_render",
    description: "Render a contract clause template with provided variables. Returns formatted text.",
    inputSchema: {
      type: "object",
      properties: {
        templateId: {
          type: "string",
          description: "Clause template ID",
        },
        variables: {
          type: "object",
          description: "Variables to substitute in the template",
          additionalProperties: {
            oneOf: [
              { type: "string" },
              { type: "number" },
              { type: "boolean" },
            ],
          },
        },
        format: {
          type: "string",
          enum: ["HTML", "MARKDOWN", "PLAIN"],
          description: "Output format",
          default: "MARKDOWN",
        },
      },
      required: ["templateId", "variables"],
    },
  },
];

// Tool handlers
export async function handleClauseTool(
  name: string,
  args: Record<string, unknown>
): Promise<unknown> {
  const client = getEngineClient();

  switch (name) {
    case "clause_render": {
      const input = ClauseRenderInputSchema.parse(args);
      return client.fetch("clause", "/render", {
        method: "POST",
        body: input,
      });
    }

    default:
      throw new Error(`Unknown clause tool: ${name}`);
  }
}


path: src/engines/deal.ts
content: /**
 * Deal Engine - 7 tools
 * Manages deal lifecycle: creation, actions, search, timeline
 */

import { z } from "zod";
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
import { getEngineClient } from "../client.js";

// Input schemas
const DealListInputSchema = z.object({
  status: z.enum(["DRAFT", "ACTIVE", "COMPLETED", "CANCELLED"]).optional(),
  limit: z.number().min(1).max(100).default(20),
  offset: z.number().min(0).default(0),
});

const DealCreateInputSchema = z.object({
  title: z.string().min(1).max(200),
  description: z.string().max(5000).optional(),
  counterpartyEmail: z.string().email().optional(),
  templateId: z.string().optional(),
  amount: z.number().positive().optional(),
  currency: z.string().default("USD"),
  dueDate: z.string().datetime().optional(),
});

const DealGetInputSchema = z.object({
  dealId: z.string().min(1),
});

const DealActionInputSchema = z.object({
  dealId: z.string().min(1),
  action: z.enum(["SIGN", "APPROVE", "REJECT", "CANCEL", "COMPLETE", "ARCHIVE"]),
  comment: z.string().max(1000).optional(),
});

const DealSearchInputSchema = z.object({
  query: z.string().min(1).max(200),
  status: z.enum(["DRAFT", "ACTIVE", "COMPLETED", "CANCELLED"]).optional(),
  fromDate: z.string().datetime().optional(),
  toDate: z.string().datetime().optional(),
  limit: z.number().min(1).max(100).default(20),
});

const DealTimelineInputSchema = z.object({
  dealId: z.string().min(1),
  limit: z.number().min(1).max(100).default(50),
});

const DealAttachmentsInputSchema = z.object({
  dealId: z.string().min(1),
});

// Tool definitions
export const dealTools: Tool[] = [
  {
    name: "deal_list",
    description: "List deals with optional status filter. Returns paginated list of deals for the authenticated user.",
    inputSchema: {
      type: "object",
      properties: {
        status: {
          type: "string",
          enum: ["DRAFT", "ACTIVE", "COMPLETED", "CANCELLED"],
          description: "Filter by deal status",
        },
        limit: {
          type: "number",
          description: "Number of deals to return (max 100)",
          default: 20,
        },
        offset: {
          type: "number",
          description: "Pagination offset",
          default: 0,
        },
      },
    },
  },
  {
    name: "deal_create",
    description: "Create a new deal. Can optionally use a template and specify counterparty.",
    inputSchema: {
      type: "object",
      properties: {
        title: {
          type: "string",
          description: "Deal title (required)",
        },
        description: {
          type: "string",
          description: "Deal description",
        },
        counterpartyEmail: {
          type: "string",
          description: "Email of the counterparty to invite",
        },
        templateId: {
          type: "string",
          description: "Template ID to base the deal on",
        },
        amount: {
          type: "number",
          description: "Deal amount",
        },
        currency: {
          type: "string",
          description: "Currency code (default: USD)",
        },
        dueDate: {
          type: "string",
          description: "Due date in ISO 8601 format",
        },
      },
      required: ["title"],
    },
  },
  {
    name: "deal_get",
    description: "Get detailed information about a specific deal including parties, terms, and current status.",
    inputSchema: {
      type: "object",
      properties: {
        dealId: {
          type: "string",
          description: "Deal ID",
        },
      },
      required: ["dealId"],
    },
  },
  {
    name: "deal_action",
    description: "Perform an action on a deal: sign, approve, reject, cancel, complete, or archive.",
    inputSchema: {
      type: "object",
      properties: {
        dealId: {
          type: "string",
          description: "Deal ID",
        },
        action: {
          type: "string",
          enum: ["SIGN", "APPROVE", "REJECT", "CANCEL", "COMPLETE", "ARCHIVE"],
          description: "Action to perform",
        },
        comment: {
          type: "string",
          description: "Optional comment for the action",
        },
      },
      required: ["dealId", "action"],
    },
  },
  {
    name: "deal_search",
    description: "Search deals by text query across titles, descriptions, and party names.",
    inputSchema: {
      type: "object",
      properties: {
        query: {
          type: "string",
          description: "Search query",
        },
        status: {
          type: "string",
          enum: ["DRAFT", "ACTIVE", "COMPLETED", "CANCELLED"],
          description: "Filter by status",
        },
        fromDate: {
          type: "string",
          description: "Start date filter (ISO 8601)",
        },
        toDate: {
          type: "string",
          description: "End date filter (ISO 8601)",
        },
        limit: {
          type: "number",
          description: "Max results",
          default: 20,
        },
      },
      required: ["query"],
    },
  },
  {
    name: "deal_timeline",
    description: "Get the activity timeline for a deal showing all events and state changes.",
    inputSchema: {
      type: "object",
      properties: {
        dealId: {
          type: "string",
          description: "Deal ID",
        },
        limit: {
          type: "number",
          description: "Max events to return",
          default: 50,
        },
      },
      required: ["dealId"],
    },
  },
  {
    name: "deal_attachments",
    description: "List all attachments (documents, files) associated with a deal.",
    inputSchema: {
      type: "object",
      properties: {
        dealId: {
          type: "string",
          description: "Deal ID",
        },
      },
      required: ["dealId"],
    },
  },
];

// Tool handlers
export async function handleDealTool(
  name: string,
  args: Record<string, unknown>
): Promise<unknown> {
  const client = getEngineClient();

  switch (name) {
    case "deal_list": {
      const input = DealListInputSchema.parse(args);
      const params = new URLSearchParams();
      if (input.status) params.set("status", input.status);
      params.set("limit", String(input.limit));
      params.set("offset", String(input.offset));
      return client.fetch("deal", `/deals?${params.toString()}`);
    }

    case "deal_create": {
      const input = DealCreateInputSchema.parse(args);
      return client.fetch("deal", "/deals", {
        method: "POST",
        body: input,
        idempotencyKey: `create-${Date.now()}`,
      });
    }

    case "deal_get": {
      const input = DealGetInputSchema.parse(args);
      return client.fetch("deal", `/deals/${input.dealId}`);
    }

    case "deal_action": {
      const input = DealActionInputSchema.parse(args);
      return client.fetch("deal", `/deals/${input.dealId}/actions`, {
        method: "POST",
        body: { action: input.action, comment: input.comment },
        idempotencyKey: `action-${input.dealId}-${input.action}-${Date.now()}`,
      });
    }

    case "deal_search": {
      const input = DealSearchInputSchema.parse(args);
      return client.fetch("deal", "/deals/search", {
        method: "POST",
        body: input,
      });
    }

    case "deal_timeline": {
      const input = DealTimelineInputSchema.parse(args);
      const params = new URLSearchParams({ limit: String(input.limit) });
      return client.fetch("deal", `/deals/${input.dealId}/timeline?${params.toString()}`);
    }

    case "deal_attachments": {
      const input = DealAttachmentsInputSchema.parse(args);
      return client.fetch("deal", `/deals/${input.dealId}/attachments`);
    }

    default:
      throw new Error(`Unknown deal tool: ${name}`);
  }
}


path: src/engines/fund.ts
content: /**
 * Fund Engine - 6 tools
 * Manages wallets, transfers, escrow, and agent funding
 */

import { z } from "zod";
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
import { getEngineClient } from "../client.js";

// Input schemas
const FundBalanceInputSchema = z.object({
  walletId: z.string().optional(),
});

const FundTransferInputSchema = z.object({
  toWalletId: z.string().min(1),
  amount: z.number().positive(),
  currency: z.string().default("TALER"),
  memo: z.string().max(500).optional(),
  idempotencyKey: z.string().optional(),
});

const FundTransactionsInputSchema = z.object({
  walletId: z.string().optional(),
  type: z.enum(["CREDIT", "DEBIT", "ESCROW_LOCK", "ESCROW_RELEASE"]).optional(),
  limit: z.number().min(1).max(100).default(50),
  offset: z.number().min(0).default(0),
});

const FundEscrowInputSchema = z.object({
  dealId: z.string().min(1),
  amount: z.number().positive(),
  currency: z.string().default("TALER"),
  releaseConditions: z.array(z.string()).optional(),
});

const FundCashoutInputSchema = z.object({
  amount: z.number().positive(),
  destinationAddress: z.string().min(1),
  network: z.enum(["BASE", "BASE_SEPOLIA"]).default("BASE_SEPOLIA"),
});

const FundAgentFundInputSchema = z.object({
  agentId: z.string().min(1),
  amount: z.number().positive(),
  purpose: z.string().max(200).optional(),
});

// Tool definitions
export const fundTools: Tool[] = [
  {
    name: "fund_balance",
    description: "Get wallet balance including available, locked (in escrow), and pending amounts.",
    inputSchema: {
      type: "object",
      properties: {
        walletId: {
          type: "string",
          description: "Wallet ID (optional, defaults to user's primary wallet)",
        },
      },
    },
  },
  {
    name: "fund_transfer",
    description: "Transfer funds between wallets. Supports Taler tokens on Base.",
    inputSchema: {
      type: "object",
      properties: {
        toWalletId: {
          type: "string",
          description: "Destination wallet ID",
        },
        amount: {
          type: "number",
          description: "Amount to transfer",
        },
        currency: {
          type: "string",
          description: "Currency (default: TALER)",
        },
        memo: {
          type: "string",
          description: "Transfer memo/note",
        },
        idempotencyKey: {
          type: "string",
          description: "Idempotency key to prevent duplicate transfers",
        },
      },
      required: ["toWalletId", "amount"],
    },
  },
  {
    name: "fund_transactions",
    description: "List transaction history with optional type filter.",
    inputSchema: {
      type: "object",
      properties: {
        walletId: {
          type: "string",
          description: "Wallet ID (optional)",
        },
        type: {
          type: "string",
          enum: ["CREDIT", "DEBIT", "ESCROW_LOCK", "ESCROW_RELEASE"],
          description: "Filter by transaction type",
        },
        limit: {
          type: "number",
          description: "Max transactions to return",
          default: 50,
        },
        offset: {
          type: "number",
          description: "Pagination offset",
          default: 0,
        },
      },
    },
  },
  {
    name: "fund_escrow",
    description: "Lock funds in escrow for a deal. Funds are released when deal conditions are met.",
    inputSchema: {
      type: "object",
      properties: {
        dealId: {
          type: "string",
          description: "Deal ID to escrow funds for",
        },
        amount: {
          type: "number",
          description: "Amount to lock",
        },
        currency: {
          type: "string",
          description: "Currency (default: TALER)",
        },
        releaseConditions: {
          type: "array",
          items: { type: "string" },
          description: "Conditions that must be met to release funds",
        },
      },
      required: ["dealId", "amount"],
    },
  },
  {
    name: "fund_cashout",
    description: "Cash out Taler tokens to external wallet address on Base network.",
    inputSchema: {
      type: "object",
      properties: {
        amount: {
          type: "number",
          description: "Amount to cash out",
        },
        destinationAddress: {
          type: "string",
          description: "Destination wallet address (0x...)",
        },
        network: {
          type: "string",
          enum: ["BASE", "BASE_SEPOLIA"],
          description: "Network (default: BASE_SEPOLIA for testnet)",
        },
      },
      required: ["amount", "destinationAddress"],
    },
  },
  {
    name: "fund_agent_fund",
    description: "Fund an agent's operational wallet for autonomous transactions.",
    inputSchema: {
      type: "object",
      properties: {
        agentId: {
          type: "string",
          description: "Agent ID to fund",
        },
        amount: {
          type: "number",
          description: "Amount to allocate",
        },
        purpose: {
          type: "string",
          description: "Purpose/reason for funding",
        },
      },
      required: ["agentId", "amount"],
    },
  },
];

// Tool handlers
export async function handleFundTool(
  name: string,
  args: Record<string, unknown>
): Promise<unknown> {
  const client = getEngineClient();

  switch (name) {
    case "fund_balance": {
      const input = FundBalanceInputSchema.parse(args);
      const path = input.walletId
        ? `/wallets/${input.walletId}/balance`
        : "/wallets/me/balance";
      return client.fetch("fund", path);
    }

    case "fund_transfer": {
      const input = FundTransferInputSchema.parse(args);
      return client.fetch("fund", "/transfers", {
        method: "POST",
        body: input,
        idempotencyKey: input.idempotencyKey ?? `transfer-${Date.now()}`,
      });
    }

    case "fund_transactions": {
      const input = FundTransactionsInputSchema.parse(args);
      const params = new URLSearchParams();
      if (input.walletId) params.set("walletId", input.walletId);
      if (input.type) params.set("type", input.type);
      params.set("limit", String(input.limit));
      params.set("offset", String(input.offset));
      return client.fetch("fund", `/transactions?${params.toString()}`);
    }

    case "fund_escrow": {
      const input = FundEscrowInputSchema.parse(args);
      return client.fetch("fund", "/escrow", {
        method: "POST",
        body: input,
        idempotencyKey: `escrow-${input.dealId}-${Date.now()}`,
      });
    }

    case "fund_cashout": {
      const input = FundCashoutInputSchema.parse(args);
      return client.fetch("fund", "/cashout", {
        method: "POST",
        body: input,
        idempotencyKey: `cashout-${Date.now()}`,
      });
    }

    case "fund_agent_fund": {
      const input = FundAgentFundInputSchema.parse(args);
      return client.fetch("fund", `/agents/${input.agentId}/fund`, {
        method: "POST",
        body: { amount: input.amount, purpose: input.purpose },
        idempotencyKey: `agent-fund-${input.agentId}-${Date.now()}`,
      });
    }

    default:
      throw new Error(`Unknown fund tool: ${name}`);
  }
}


path: tsconfig.json
content: {
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "lib": ["ES2022"],
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "declarationMap": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "tests"]
}


path: tsup.config.ts
content: import { defineConfig } from "tsup";

export default defineConfig({
  entry: ["src/index.ts"],
  format: ["esm"],
  dts: true,
  clean: true,
  sourcemap: true,
  minify: false,
  target: "node18",
  banner: {
    js: "#!/usr/bin/env node",
  },
});


Security scan verdicts — ClawScan: suspicious | static analysis: suspicious | VirusTotal: suspicious | SkillSpector: suspicious (MEDIUM)