@scriptmasterlabs/mcp-x402 2.0.2 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.well-known/agentcard.json +34 -34
  2. package/.well-known/ai.txt +32 -0
  3. package/CONTRIBUTING.md +76 -76
  4. package/LICENSE +21 -21
  5. package/README.md +304 -304
  6. package/agents.json +81 -67
  7. package/ai/faq.json +74 -0
  8. package/ai/summary.json +157 -0
  9. package/dist/lib/chains/base.d.ts.map +1 -1
  10. package/dist/lib/chains/base.js +2 -0
  11. package/dist/lib/chains/base.js.map +1 -1
  12. package/dist/lib/credit/bureau.d.ts +7 -1
  13. package/dist/lib/credit/bureau.d.ts.map +1 -1
  14. package/dist/lib/credit/bureau.js +40 -10
  15. package/dist/lib/credit/bureau.js.map +1 -1
  16. package/dist/server/index.js +128 -5
  17. package/dist/server/index.js.map +1 -1
  18. package/llms.txt +170 -70
  19. package/package.json +78 -78
  20. package/server.json +52 -48
  21. package/.env.example +0 -35
  22. package/.github/workflows/ci.yml +0 -59
  23. package/.github/workflows/keepalive.yml +0 -31
  24. package/Dockerfile +0 -19
  25. package/docker-compose.yml +0 -50
  26. package/mcp-publisher.exe +0 -0
  27. package/render.yaml +0 -39
  28. package/sdk/mcp-x402-sdk/package.json +0 -18
  29. package/sdk/mcp-x402-sdk/src/index.ts +0 -118
  30. package/sdk/mcp-x402-sdk/tsconfig.json +0 -14
  31. package/services/backtest_service.py +0 -176
  32. package/src/lib/chains/base.ts +0 -77
  33. package/src/lib/chains/solana.ts +0 -59
  34. package/src/lib/chains/xrpl.ts +0 -63
  35. package/src/lib/credit/bureau.ts +0 -65
  36. package/src/lib/sml-api/agentcard.ts +0 -40
  37. package/src/lib/sml-api/backtest.ts +0 -47
  38. package/src/lib/sml-api/brokers.ts +0 -160
  39. package/src/lib/sml-api/copytrader.ts +0 -33
  40. package/src/lib/sml-api/crawl.ts +0 -44
  41. package/src/lib/sml-api/echo.ts +0 -28
  42. package/src/lib/sml-api/forge.ts +0 -33
  43. package/src/lib/sml-api/ftd.ts +0 -53
  44. package/src/lib/sml-api/ghost.ts +0 -35
  45. package/src/lib/sml-api/launchpad.ts +0 -43
  46. package/src/lib/sml-api/leviathan.ts +0 -49
  47. package/src/lib/sml-api/nexus.ts +0 -50
  48. package/src/lib/sml-api/proof402.ts +0 -27
  49. package/src/lib/sml-api/rails.ts +0 -34
  50. package/src/lib/sml-api/shadow.ts +0 -35
  51. package/src/lib/sml-api/squeezeos.ts +0 -95
  52. package/src/lib/sml-api/xdeo.ts +0 -40
  53. package/src/lib/sml-api/xmit.ts +0 -40
  54. package/src/server/health.ts +0 -52
  55. package/src/server/index.ts +0 -213
  56. package/src/server/payments/ap2.ts +0 -101
  57. package/src/server/payments/receipt.ts +0 -85
  58. package/src/server/payments/router.ts +0 -110
  59. package/src/server/payments/wallet.ts +0 -123
  60. package/src/server/payments/x402.ts +0 -177
  61. package/src/server/registry/catalog.ts +0 -61
  62. package/src/server/registry/discovery.ts +0 -39
  63. package/src/server/registry/pricing.ts +0 -133
  64. package/src/server/security/acl.ts +0 -42
  65. package/src/server/security/audit.ts +0 -94
  66. package/src/server/security/rate-limit.ts +0 -84
  67. package/src/server/security/sandbox.ts +0 -40
  68. package/src/server/tools/agentcard.ts +0 -134
  69. package/src/server/tools/backtest.ts +0 -119
  70. package/src/server/tools/brokers.ts +0 -250
  71. package/src/server/tools/copytrader.ts +0 -104
  72. package/src/server/tools/crawl.ts +0 -70
  73. package/src/server/tools/discovery.ts +0 -202
  74. package/src/server/tools/echo.ts +0 -58
  75. package/src/server/tools/forge.ts +0 -87
  76. package/src/server/tools/ftd.ts +0 -88
  77. package/src/server/tools/ghost.ts +0 -93
  78. package/src/server/tools/index.ts +0 -42
  79. package/src/server/tools/launchpad.ts +0 -173
  80. package/src/server/tools/leviathan.ts +0 -81
  81. package/src/server/tools/nexus.ts +0 -76
  82. package/src/server/tools/proof402.ts +0 -87
  83. package/src/server/tools/rails.ts +0 -92
  84. package/src/server/tools/shadow.ts +0 -128
  85. package/src/server/tools/squeezeos.ts +0 -312
  86. package/src/server/tools/xdeo.ts +0 -67
  87. package/src/server/tools/xmit.ts +0 -68
  88. package/tests/integration/e2e.test.ts +0 -51
  89. package/tests/unit/payments.test.ts +0 -49
  90. package/tests/unit/security.test.ts +0 -92
  91. package/tests/unit/tools.test.ts +0 -42
  92. package/tsconfig.json +0 -21
  93. package/vitest.config.ts +0 -20
@@ -1,53 +0,0 @@
1
- export interface FtdAlertParams {
2
- ticker?: string;
3
- minSpikeMultiplier: number;
4
- }
5
-
6
- export interface FtdScanParams {
7
- ticker?: string;
8
- scanType: 'full' | 'spike_history';
9
- minSpikeMultiplier: number;
10
- }
11
-
12
- export class FtdClient {
13
- private static instance: FtdClient;
14
- private readonly baseUrl: string;
15
-
16
- private constructor() {
17
- this.baseUrl = process.env['SML_API_BASE'] ?? 'https://api.scriptmasterlabs.com';
18
- }
19
-
20
- static getInstance(): FtdClient {
21
- if (!FtdClient.instance) {
22
- FtdClient.instance = new FtdClient();
23
- }
24
- return FtdClient.instance;
25
- }
26
-
27
- async getAlerts(params: FtdAlertParams): Promise<unknown> {
28
- const qs = new URLSearchParams({ min_spike: String(params.minSpikeMultiplier) });
29
- if (params.ticker) qs.set('ticker', params.ticker);
30
-
31
- const res = await fetch(`${this.baseUrl}/ftd/v1/alerts?${qs.toString()}`, {
32
- signal: AbortSignal.timeout(10_000),
33
- });
34
-
35
- if (!res.ok) throw new Error(`FTD API error: HTTP ${res.status}`);
36
- return res.json();
37
- }
38
-
39
- async getFullScan(params: FtdScanParams): Promise<unknown> {
40
- const qs = new URLSearchParams({
41
- scan_type: params.scanType,
42
- min_spike: String(params.minSpikeMultiplier),
43
- });
44
- if (params.ticker) qs.set('ticker', params.ticker);
45
-
46
- const res = await fetch(`${this.baseUrl}/ftd/v1/scan?${qs.toString()}`, {
47
- signal: AbortSignal.timeout(10_000),
48
- });
49
-
50
- if (!res.ok) throw new Error(`FTD API error: HTTP ${res.status}`);
51
- return res.json();
52
- }
53
- }
@@ -1,35 +0,0 @@
1
- const GHOST_BASE = process.env['GHOST_LAYER_URL'] ?? 'https://ghost-layer.onrender.com';
2
-
3
- async function ghostGet(path: string): Promise<unknown> {
4
- const res = await fetch(`${GHOST_BASE}${path}`, {
5
- headers: { 'Accept': 'application/json' },
6
- signal: AbortSignal.timeout(15_000),
7
- });
8
- if (!res.ok) throw new Error(`Ghost Layer GET ${path}: HTTP ${res.status}`);
9
- return res.json();
10
- }
11
-
12
- export interface GhostRouteParams {
13
- fromChain: 'xrpl' | 'base';
14
- toChain: 'xrpl' | 'base';
15
- amount: string;
16
- currency: string;
17
- destinationAddress: string;
18
- walletAddress: string;
19
- }
20
-
21
- async function ghostPost(path: string, body: unknown): Promise<unknown> {
22
- const res = await fetch(`${GHOST_BASE}${path}`, {
23
- method: 'POST',
24
- headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
25
- body: JSON.stringify(body),
26
- signal: AbortSignal.timeout(15_000),
27
- });
28
- if (!res.ok) throw new Error(`Ghost Layer POST ${path}: HTTP ${res.status}`);
29
- return res.json();
30
- }
31
-
32
- export const GhostLayerAPI = {
33
- status: () => ghostGet('/api/status'),
34
- route: (params: GhostRouteParams) => ghostPost('/api/route', params),
35
- };
@@ -1,43 +0,0 @@
1
- const LAUNCHPAD_BASE = process.env['SML_LAUNCHPAD_URL'] ?? 'https://sml-launchpad.onrender.com';
2
-
3
- async function lpGet(path: string): Promise<unknown> {
4
- const res = await fetch(`${LAUNCHPAD_BASE}${path}`, {
5
- headers: { 'Accept': 'application/json' },
6
- signal: AbortSignal.timeout(15_000),
7
- });
8
- if (!res.ok) throw new Error(`Launchpad GET ${path}: HTTP ${res.status}`);
9
- return res.json();
10
- }
11
-
12
- export interface LaunchpadCreateParams {
13
- name: string;
14
- symbol: string;
15
- description: string;
16
- creatorAddress: string;
17
- initialSupply: number;
18
- targetLiquidityXrp: number;
19
- }
20
-
21
- export interface LaunchpadBuyParams {
22
- tokenAddress: string;
23
- buyerAddress: string;
24
- xrpAmount: number;
25
- }
26
-
27
- async function lpPost(path: string, body: unknown): Promise<unknown> {
28
- const res = await fetch(`${LAUNCHPAD_BASE}${path}`, {
29
- method: 'POST',
30
- headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
31
- body: JSON.stringify(body),
32
- signal: AbortSignal.timeout(20_000),
33
- });
34
- if (!res.ok) throw new Error(`Launchpad POST ${path}: HTTP ${res.status}`);
35
- return res.json();
36
- }
37
-
38
- export const LaunchpadAPI = {
39
- status: () => lpGet('/status'),
40
- list: () => lpGet('/tokens'),
41
- create: (params: LaunchpadCreateParams) => lpPost('/tokens', params),
42
- buy: (params: LaunchpadBuyParams) => lpPost('/tokens/buy', params),
43
- };
@@ -1,49 +0,0 @@
1
- export interface LeviathanSignalParams {
2
- ticker?: string;
3
- signalType: 'squeeze' | 'momentum' | 'dark_pool' | 'all';
4
- minConfidence: number;
5
- }
6
-
7
- export interface LeviathanSignal {
8
- ticker: string;
9
- signal_type: string;
10
- confidence: number;
11
- directive: string;
12
- regime: string;
13
- timestamp: string;
14
- source: string;
15
- }
16
-
17
- export class LeviathanClient {
18
- private static instance: LeviathanClient;
19
- private readonly baseUrl: string;
20
-
21
- private constructor() {
22
- this.baseUrl = process.env['SML_API_BASE'] ?? 'https://api.scriptmasterlabs.com';
23
- }
24
-
25
- static getInstance(): LeviathanClient {
26
- if (!LeviathanClient.instance) {
27
- LeviathanClient.instance = new LeviathanClient();
28
- }
29
- return LeviathanClient.instance;
30
- }
31
-
32
- async getSignal(params: LeviathanSignalParams): Promise<LeviathanSignal[]> {
33
- const qs = new URLSearchParams({
34
- signal_type: params.signalType,
35
- min_confidence: String(params.minConfidence),
36
- });
37
- if (params.ticker) qs.set('ticker', params.ticker);
38
-
39
- const res = await fetch(`${this.baseUrl}/leviathan/v1/signal?${qs.toString()}`, {
40
- signal: AbortSignal.timeout(10_000),
41
- });
42
-
43
- if (!res.ok) {
44
- throw new Error(`Leviathan API error: HTTP ${res.status}`);
45
- }
46
-
47
- return (await res.json()) as LeviathanSignal[];
48
- }
49
- }
@@ -1,50 +0,0 @@
1
- export interface NexusQueryParams {
2
- capability: string;
3
- maxBudget: string;
4
- }
5
-
6
- export interface NexusHireParams {
7
- agentId: string;
8
- budget: string;
9
- chainPreference?: string;
10
- }
11
-
12
- export class NexusClient {
13
- private static instance: NexusClient;
14
- private readonly baseUrl: string;
15
-
16
- private constructor() {
17
- this.baseUrl = process.env['SML_API_BASE'] ?? 'https://api.scriptmasterlabs.com';
18
- }
19
-
20
- static getInstance(): NexusClient {
21
- if (!NexusClient.instance) {
22
- NexusClient.instance = new NexusClient();
23
- }
24
- return NexusClient.instance;
25
- }
26
-
27
- async queryAgents(params: NexusQueryParams): Promise<unknown> {
28
- const res = await fetch(`${this.baseUrl}/nexus/v1/agents`, {
29
- method: 'POST',
30
- headers: { 'Content-Type': 'application/json' },
31
- body: JSON.stringify({ capability: params.capability, max_budget: params.maxBudget }),
32
- signal: AbortSignal.timeout(10_000),
33
- });
34
-
35
- if (!res.ok) throw new Error(`Nexus API error: HTTP ${res.status}`);
36
- return res.json();
37
- }
38
-
39
- async hireAgent(params: NexusHireParams): Promise<unknown> {
40
- const res = await fetch(`${this.baseUrl}/nexus/v1/agents/${params.agentId}/hire`, {
41
- method: 'POST',
42
- headers: { 'Content-Type': 'application/json' },
43
- body: JSON.stringify({ budget: params.budget, chain_preference: params.chainPreference }),
44
- signal: AbortSignal.timeout(10_000),
45
- });
46
-
47
- if (!res.ok) throw new Error(`Nexus hire error: HTTP ${res.status}`);
48
- return res.json();
49
- }
50
- }
@@ -1,27 +0,0 @@
1
- const PROOF402_BASE = process.env['PROOF402_SERVER_URL'] ?? 'https://four02proof.onrender.com';
2
-
3
- async function proofGet(path: string): Promise<unknown> {
4
- const res = await fetch(`${PROOF402_BASE}${path}`, {
5
- headers: { 'Accept': 'application/json' },
6
- signal: AbortSignal.timeout(15_000),
7
- });
8
- if (!res.ok) throw new Error(`402Proof GET ${path}: HTTP ${res.status}`);
9
- return res.json();
10
- }
11
-
12
- async function proofPost(path: string, body: unknown): Promise<unknown> {
13
- const res = await fetch(`${PROOF402_BASE}${path}`, {
14
- method: 'POST',
15
- headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
16
- body: JSON.stringify(body),
17
- signal: AbortSignal.timeout(15_000),
18
- });
19
- if (!res.ok) throw new Error(`402Proof POST ${path}: HTTP ${res.status}`);
20
- return res.json();
21
- }
22
-
23
- export const Proof402API = {
24
- invoice: (endpointId: string) => proofGet(`/v1/invoice/${encodeURIComponent(endpointId)}`),
25
- verify: (txHash: string, endpointId: string) => proofPost('/v1/verify', { tx_hash: txHash, endpoint_id: endpointId }),
26
- creditScore: (walletAddress: string) => proofGet(`/v1/credit/${encodeURIComponent(walletAddress)}`),
27
- };
@@ -1,34 +0,0 @@
1
- const RAILS_BASE = process.env['SML_RAILS_URL'] ?? 'https://sml-rails.onrender.com';
2
-
3
- async function railsGet(path: string): Promise<unknown> {
4
- const res = await fetch(`${RAILS_BASE}${path}`, {
5
- headers: { 'Accept': 'application/json' },
6
- signal: AbortSignal.timeout(15_000),
7
- });
8
- if (!res.ok) throw new Error(`SML Rails GET ${path}: HTTP ${res.status}`);
9
- return res.json();
10
- }
11
-
12
- export interface RailsTransferParams {
13
- fromAddress: string;
14
- toAddress: string;
15
- amount: string;
16
- currency: 'RLUSD' | 'XRP';
17
- memo?: string;
18
- }
19
-
20
- async function railsPost(path: string, body: unknown): Promise<unknown> {
21
- const res = await fetch(`${RAILS_BASE}${path}`, {
22
- method: 'POST',
23
- headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' },
24
- body: JSON.stringify(body),
25
- signal: AbortSignal.timeout(20_000),
26
- });
27
- if (!res.ok) throw new Error(`SML Rails POST ${path}: HTTP ${res.status}`);
28
- return res.json();
29
- }
30
-
31
- export const RailsAPI = {
32
- status: () => railsGet('/status'),
33
- transfer: (params: RailsTransferParams) => railsPost('/transfer', params),
34
- };
@@ -1,35 +0,0 @@
1
- const SHADOW_BASE = process.env['SHADOW_DESK_URL'] ?? 'https://shadow-desk.onrender.com';
2
- const SHADOW_ADMIN_KEY = process.env['SHADOW_ADMIN_API_KEY'] ?? '';
3
-
4
- async function shadowPost(path: string, body: unknown): Promise<unknown> {
5
- const headers: Record<string, string> = {
6
- 'Accept': 'application/json',
7
- 'Content-Type': 'application/json',
8
- };
9
- if (SHADOW_ADMIN_KEY) headers['X-Admin-Key'] = SHADOW_ADMIN_KEY;
10
- const res = await fetch(`${SHADOW_BASE}${path}`, {
11
- method: 'POST',
12
- headers,
13
- body: JSON.stringify(body),
14
- signal: AbortSignal.timeout(15_000),
15
- });
16
- if (!res.ok) throw new Error(`Shadow Desk POST ${path}: HTTP ${res.status}`);
17
- return res.json();
18
- }
19
-
20
- export interface ShadowQueryParams {
21
- query: string;
22
- context?: string;
23
- walletAddress: string;
24
- }
25
-
26
- export interface ShadowIngestParams {
27
- source: string;
28
- payload: unknown;
29
- walletAddress: string;
30
- }
31
-
32
- export const ShadowDeskAPI = {
33
- query: (params: ShadowQueryParams) => shadowPost('/query', params),
34
- ingest: (params: ShadowIngestParams) => shadowPost('/ingest', params),
35
- };
@@ -1,95 +0,0 @@
1
- import { createHmac } from 'crypto';
2
-
3
- const SQUEEZEOS_BASE = process.env['SQUEEZEOS_BASE_URL'] ?? 'https://squeezeos-api.onrender.com';
4
- const PROOF402_SECRET = process.env['PROOF402_TOKEN_SECRET'] ?? '';
5
-
6
- // Endpoint UUIDs — override via env vars if they change
7
- const ENDPOINT_UUIDS: Record<string, string> = {
8
- council: process.env['SQUEEZEOS_UUID_COUNCIL'] ?? '12a0e7a1-0000-0000-0000-000000000001',
9
- scan: process.env['SQUEEZEOS_UUID_SCAN'] ?? '160cf28d-0000-0000-0000-000000000002',
10
- options: process.env['SQUEEZEOS_UUID_OPTIONS'] ?? 'c951a374-0000-0000-0000-000000000003',
11
- iwm: process.env['SQUEEZEOS_UUID_IWM'] ?? '60f48ce0-0000-0000-0000-000000000004',
12
- marketplace_read: process.env['SQUEEZEOS_UUID_MARKETPLACE_READ'] ?? 'd1a2b3c4-0000-0000-0000-000000000005',
13
- };
14
-
15
- /**
16
- * Generate a SqueezeOS JWT for a premium endpoint.
17
- * Format: base64(json).HMAC-SHA256(secret, encoded)
18
- */
19
- export function generateSqueezeOSToken(endpointKey: string, walletAddress: string): string {
20
- const eid = ENDPOINT_UUIDS[endpointKey];
21
- if (!eid) throw new Error(`Unknown SqueezeOS endpoint key: ${endpointKey}`);
22
- if (!PROOF402_SECRET) throw new Error('PROOF402_TOKEN_SECRET is not set — cannot mint SqueezeOS JWT');
23
-
24
- const payload = {
25
- eid,
26
- wlt: walletAddress,
27
- iid: `mcp-x402-${Date.now()}`,
28
- exp: Math.floor(Date.now() / 1000) + 3600, // 1 hour
29
- };
30
-
31
- const encoded = Buffer.from(JSON.stringify(payload)).toString('base64url');
32
- const sig = createHmac('sha256', PROOF402_SECRET).update(encoded).digest('hex');
33
- return `${encoded}.${sig}`;
34
- }
35
-
36
- async function squeezeGet(path: string, token?: string): Promise<unknown> {
37
- const headers: Record<string, string> = { 'Accept': 'application/json' };
38
- if (token) headers['X-Payment-Token'] = token;
39
- const res = await fetch(`${SQUEEZEOS_BASE}${path}`, {
40
- headers,
41
- signal: AbortSignal.timeout(15_000),
42
- });
43
- if (!res.ok) throw new Error(`SqueezeOS GET ${path}: HTTP ${res.status}`);
44
- return res.json();
45
- }
46
-
47
- async function squeezePost(path: string, body: unknown, token?: string): Promise<unknown> {
48
- const headers: Record<string, string> = {
49
- 'Accept': 'application/json',
50
- 'Content-Type': 'application/json',
51
- };
52
- if (token) headers['X-Payment-Token'] = token;
53
- const res = await fetch(`${SQUEEZEOS_BASE}${path}`, {
54
- method: 'POST',
55
- headers,
56
- body: JSON.stringify(body),
57
- signal: AbortSignal.timeout(15_000),
58
- });
59
- if (!res.ok) throw new Error(`SqueezeOS POST ${path}: HTTP ${res.status}`);
60
- return res.json();
61
- }
62
-
63
- export const SqueezeOSAPI = {
64
- // FREE
65
- preview: (symbol: string) => squeezeGet(`/api/preview/${encodeURIComponent(symbol)}`),
66
- history: (symbol?: string) => squeezeGet(symbol ? `/api/history/${encodeURIComponent(symbol)}` : '/api/history'),
67
- oracle: (symbol?: string) => squeezeGet(symbol ? `/api/oracle/${encodeURIComponent(symbol)}` : '/api/oracle'),
68
- ftd: () => squeezeGet('/api/ftd'),
69
- status: () => squeezeGet('/api/status'),
70
- demo: () => squeezeGet('/api/demo'),
71
- marketplaceBrowse: () => squeezeGet('/api/marketplace'),
72
- futuresLeaderboard: () => squeezeGet('/api/futures/leaderboard'),
73
-
74
- // PAID — caller must supply walletAddress so we can mint the token
75
- council: (symbol: string, walletAddress: string) => {
76
- const token = generateSqueezeOSToken('council', walletAddress);
77
- return squeezePost('/api/council', { symbol }, token);
78
- },
79
- scan: (walletAddress: string) => {
80
- const token = generateSqueezeOSToken('scan', walletAddress);
81
- return squeezeGet('/api/scan', token);
82
- },
83
- options: (walletAddress: string) => {
84
- const token = generateSqueezeOSToken('options', walletAddress);
85
- return squeezeGet('/api/options', token);
86
- },
87
- iwm: (walletAddress: string) => {
88
- const token = generateSqueezeOSToken('iwm', walletAddress);
89
- return squeezeGet('/api/iwm', token);
90
- },
91
- marketplaceRead: (listingId: string, walletAddress: string) => {
92
- const token = generateSqueezeOSToken('marketplace_read', walletAddress);
93
- return squeezePost('/api/marketplace/read', { listing_id: listingId }, token);
94
- },
95
- };
@@ -1,40 +0,0 @@
1
- export interface XdeoEstimateParams {
2
- ticker: string;
3
- fiscalQuarter: string;
4
- estimateType: 'eps' | 'revenue' | 'guidance' | 'all';
5
- }
6
-
7
- export class XdeoClient {
8
- private static instance: XdeoClient;
9
- private readonly baseUrl: string;
10
-
11
- private constructor() {
12
- this.baseUrl = process.env['SML_API_BASE'] ?? 'https://api.scriptmasterlabs.com';
13
- }
14
-
15
- static getInstance(): XdeoClient {
16
- if (!XdeoClient.instance) {
17
- XdeoClient.instance = new XdeoClient();
18
- }
19
- return XdeoClient.instance;
20
- }
21
-
22
- async getEstimate(params: XdeoEstimateParams): Promise<unknown> {
23
- const res = await fetch(`${this.baseUrl}/xdeo/v1/estimate`, {
24
- method: 'POST',
25
- headers: { 'Content-Type': 'application/json' },
26
- body: JSON.stringify({
27
- ticker: params.ticker,
28
- fiscal_quarter: params.fiscalQuarter,
29
- estimate_type: params.estimateType,
30
- }),
31
- signal: AbortSignal.timeout(10_000),
32
- });
33
-
34
- if (!res.ok) {
35
- throw new Error(`xDEO API error: HTTP ${res.status}`);
36
- }
37
-
38
- return res.json();
39
- }
40
- }
@@ -1,40 +0,0 @@
1
- export interface XmitDecodeParams {
2
- filingUrl: string;
3
- parseTarget: 'executive_pay' | 'holdings' | 'ownership_changes' | 'all';
4
- format: 'json' | 'markdown';
5
- }
6
-
7
- export class XmitClient {
8
- private static instance: XmitClient;
9
- private readonly baseUrl: string;
10
-
11
- private constructor() {
12
- this.baseUrl = process.env['SML_API_BASE'] ?? 'https://api.scriptmasterlabs.com';
13
- }
14
-
15
- static getInstance(): XmitClient {
16
- if (!XmitClient.instance) {
17
- XmitClient.instance = new XmitClient();
18
- }
19
- return XmitClient.instance;
20
- }
21
-
22
- async decode(params: XmitDecodeParams): Promise<unknown> {
23
- const res = await fetch(`${this.baseUrl}/xmit/v1/decode`, {
24
- method: 'POST',
25
- headers: { 'Content-Type': 'application/json' },
26
- body: JSON.stringify({
27
- filing_url: params.filingUrl,
28
- parse_target: params.parseTarget,
29
- format: params.format,
30
- }),
31
- signal: AbortSignal.timeout(30_000),
32
- });
33
-
34
- if (!res.ok) {
35
- throw new Error(`xMIT API error: HTTP ${res.status}`);
36
- }
37
-
38
- return res.json();
39
- }
40
- }
@@ -1,52 +0,0 @@
1
- import type { Request, Response } from 'express';
2
-
3
- const startTime = Date.now();
4
-
5
- export interface HealthStatus {
6
- status: 'ok' | 'degraded';
7
- version: string;
8
- transport: string;
9
- uptime_seconds: number;
10
- uptime_human: string;
11
- timestamp: string;
12
- checks: {
13
- process: 'ok';
14
- memory_mb: number;
15
- memory_ok: boolean;
16
- };
17
- }
18
-
19
- function formatUptime(ms: number): string {
20
- const s = Math.floor(ms / 1000);
21
- const m = Math.floor(s / 60);
22
- const h = Math.floor(m / 60);
23
- const d = Math.floor(h / 24);
24
- if (d > 0) return `${d}d ${h % 24}h ${m % 60}m`;
25
- if (h > 0) return `${h}h ${m % 60}m ${s % 60}s`;
26
- if (m > 0) return `${m}m ${s % 60}s`;
27
- return `${s}s`;
28
- }
29
-
30
- export function healthHandler(_req: Request, res: Response): void {
31
- const uptimeMs = Date.now() - startTime;
32
- const memMb = Math.round(process.memoryUsage().rss / 1024 / 1024);
33
- const memOk = memMb < 450; // warn if approaching 512MB container limit
34
-
35
- const body: HealthStatus = {
36
- status: memOk ? 'ok' : 'degraded',
37
- version: process.env['npm_package_version'] ?? '1.0.0',
38
- transport: process.env['MCP_TRANSPORT'] ?? 'stdio',
39
- uptime_seconds: Math.floor(uptimeMs / 1000),
40
- uptime_human: formatUptime(uptimeMs),
41
- timestamp: new Date().toISOString(),
42
- checks: {
43
- process: 'ok',
44
- memory_mb: memMb,
45
- memory_ok: memOk,
46
- },
47
- };
48
-
49
- // Return 200 even if degraded — let the orchestrator decide.
50
- // Only return 5xx if the process itself is fundamentally broken.
51
- res.status(200).json(body);
52
- }