Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 125 additions & 131 deletions README.md

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@cpzai/mcp-server",
"version": "1.0.0",
"description": "CPZAI MCP server — AI agent access to quantitative trading strategies, backtests, multi-broker order routing, portfolios, and risk analytics.",
"version": "1.2.0",
"description": "CPZAI MCP server \u2014 AI agent access to quantitative trading strategies, backtests, multi-broker order routing, portfolios, and risk analytics.",
"license": "MIT",
"homepage": "https://ai.cpz-lab.com",
"repository": {
Expand Down Expand Up @@ -29,7 +29,8 @@
"start": "node dist/index.js",
"dev": "tsx src/index.ts",
"test": "vitest run",
"test:watch": "vitest"
"test:watch": "vitest",
"export:catalog": "tsx scripts/export-tool-catalog.ts"
},
"dependencies": {
"@modelcontextprotocol/sdk": "^1.12.1",
Expand Down
73 changes: 73 additions & 0 deletions scripts/export-tool-catalog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/** Export documentation metadata from the tools advertised over MCP itself. */
import { writeFile, mkdir } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import type { Request } from 'express';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js';
import type { ToolAnnotations } from '@modelcontextprotocol/sdk/types.js';
import { createMcpServer } from '../src/server.js';

export interface ToolCatalogEntry {
name: string;
title?: string;
description?: string;
annotations?: ToolAnnotations;
}

export async function readToolCatalog(): Promise<ToolCatalogEntry[]> {
// Discovery needs no credentials and never invokes a tool or the REST API.
const server = createMcpServer({ headers: {} } as Request);
const client = new Client({ name: 'cpzai-docs-catalog', version: '1.0.0' });
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
try {
await server.connect(serverTransport);
await client.connect(clientTransport);
const catalog: ToolCatalogEntry[] = [];
let cursor: string | undefined;
const seenCursors = new Set<string>();
do {
const page = await client.listTools(cursor ? { cursor } : undefined);
for (const tool of page.tools) {
catalog.push({
name: tool.name,
...(tool.title !== undefined ? { title: tool.title } : {}),
...(tool.description !== undefined ? { description: tool.description } : {}),
...(tool.annotations !== undefined ? { annotations: tool.annotations } : {}),
});
}
cursor = page.nextCursor;
if (cursor !== undefined) {
if (seenCursors.has(cursor) || seenCursors.size >= 100) throw new Error('Tool discovery pagination did not terminate.');
seenCursors.add(cursor);
}
} while (cursor !== undefined);
return catalog.sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
} finally {
await client.close();
await server.close();
}
}

async function main() {
const args = process.argv.slice(2);
if (args.length !== 0 && (args.length !== 2 || args[0] !== '--output' || !args[1])) {
throw new Error('Usage: tsx scripts/export-tool-catalog.ts [--output path]');
}
const destination = args.length === 0
? fileURLToPath(new URL('../tool-catalog.json', import.meta.url))
: resolve(args[1]);
const catalog = await readToolCatalog();
await mkdir(dirname(destination), { recursive: true });
await writeFile(destination, `${JSON.stringify(catalog, null, 2)}\n`, 'utf8');
console.log(`Exported ${catalog.length} MCP tools to ${destination}`);
}

if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
// The imported OAuth module owns a long-lived sweep timer. This one-shot
// exporter exits only after discovery, transport cleanup, and the write finish.
main().then(() => process.exit(0)).catch(error => {
console.error('Tool catalog export failed:', error);
process.exit(1);
});
}
10 changes: 7 additions & 3 deletions server.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
"name": "com.cpz-lab.mcp/cpzai",
"title": "CPZAI",
"description": "Build, backtest, and deploy quantitative trading strategies from your AI agent.",
"description": "Manage strategies, inspect saved backtests, trade, and review portfolio and middle-office data.",
"websiteUrl": "https://ai.cpz-lab.com",
"repository": {
"url": "https://github.com/CPZ-Lab/cpzai-mcp-server",
"source": "github"
},
"version": "1.1.0",
"version": "1.2.0",
"remotes": [
{
"type": "streamable-http",
Expand All @@ -19,7 +19,11 @@
"io.modelcontextprotocol.registry/publisher-provided": {
"tool": "manual-publish",
"version": "1.1.0",
"categories": ["finance", "trading", "quant"],
"categories": [
"finance",
"trading",
"quant"
],
"tags": [
"trading",
"quant",
Expand Down
Loading
Loading