data fixes, partial custom indicator support

This commit is contained in:
2026-04-08 21:28:31 -04:00
parent b701554996
commit a70dcd954f
81 changed files with 5438 additions and 1852 deletions

View File

@@ -55,6 +55,20 @@ export class ContainerSync {
this.logger = logger.child({ component: 'ContainerSync' });
}
/**
* Parse a raw MCP callTool response into the tool's return value.
* MCP tool results are wrapped as: { content: [{ type: 'text', text: '<json>' }] }
*/
private parseMcpResult(raw: unknown): unknown {
const r = raw as any;
const text = r?.content?.[0]?.text ?? r?.[0]?.text;
if (typeof text === 'string') {
return JSON.parse(text);
}
// Already unwrapped (shouldn't happen in practice)
return raw;
}
/**
* Load a workspace store from the container.
* Returns the stored state or indicates the store doesn't exist.
@@ -68,7 +82,7 @@ export class ContainerSync {
try {
this.logger.debug({ store: storeName }, 'Loading store from container');
const result = (await this.mcpClient.callTool('workspace_read', {
const result = this.parseMcpResult(await this.mcpClient.callTool('workspace_read', {
store_name: storeName,
})) as { exists: boolean; data?: unknown; error?: string };
@@ -104,7 +118,7 @@ export class ContainerSync {
try {
this.logger.debug({ store: storeName }, 'Saving store to container');
const result = (await this.mcpClient.callTool('workspace_write', {
const result = this.parseMcpResult(await this.mcpClient.callTool('workspace_write', {
store_name: storeName,
data: state,
})) as { success: boolean; error?: string };
@@ -136,7 +150,7 @@ export class ContainerSync {
try {
this.logger.debug({ store: storeName, patchOps: patch.length }, 'Patching store in container');
const result = (await this.mcpClient.callTool('workspace_patch', {
const result = this.parseMcpResult(await this.mcpClient.callTool('workspace_patch', {
store_name: storeName,
patch,
})) as { success: boolean; data?: unknown; error?: string };

View File

@@ -59,12 +59,12 @@ class SyncEntry {
/**
* Set state directly (used for loading from container).
* Resets sequence to 0.
* Sets sequence to 1 so clients at seq 0 (empty state) receive a full snapshot.
*/
setState(newState: unknown): void {
this.state = deepClone(newState);
this.lastSnapshot = deepClone(newState);
this.seq = 0;
this.seq = 1;
this.history = [];
}

View File

@@ -272,12 +272,84 @@ export interface Shape {
*/
export type ShapesStore = Record<string, Shape>;
/**
* Parameter schema entry for a custom indicator.
*/
export interface CustomIndicatorParam {
type: 'int' | 'float' | 'bool' | 'string';
default: any;
description?: string;
min?: number;
max?: number;
}
/**
* Per-series plot configuration for a custom indicator output column.
* style maps to LineStudyPlotStyle: 0=Line, 1=Histogram, 3=Dots/Cross,
* 4=Area, 5=Columns, 6=Circles, 9=StepLine.
*/
export interface PlotConfig {
style: number;
color?: string;
linewidth?: number;
visible?: boolean;
}
/**
* Shaded region between two plots ("plot_plot") or two bands ("hline_hline").
*/
export interface FilledAreaConfig {
id: string;
type: 'plot_plot' | 'hline_hline';
series1: string;
series2: string;
color?: string;
opacity?: number;
}
/**
* Horizontal reference line (e.g. RSI overbought/oversold level).
* linestyle: 0=solid, 1=dotted, 2=dashed.
*/
export interface BandConfig {
id: string;
value: number;
color?: string;
linewidth?: number;
linestyle?: number;
visible?: boolean;
}
/**
* Output column descriptor for a custom indicator.
*/
export interface CustomIndicatorColumn {
name: string;
display_name?: string;
description?: string;
plot?: PlotConfig;
}
/**
* Metadata needed to auto-construct a TradingView custom study.
* Populated by the indicator subagent when adding a custom_ indicator.
*/
export interface CustomIndicatorMetadata {
display_name: string;
parameters: Record<string, CustomIndicatorParam>;
input_series: string[];
output_columns: CustomIndicatorColumn[];
pane: 'price' | 'separate';
filled_areas?: FilledAreaConfig[];
bands?: BandConfig[];
}
/**
* Indicator instance on TradingView chart.
*/
export interface IndicatorInstance {
id: string;
talib_name: string;
pandas_ta_name: string;
instance_name: string;
parameters: Record<string, any>;
tv_study_id?: string;
@@ -289,6 +361,8 @@ export interface IndicatorInstance {
created_at?: number;
modified_at?: number;
original_id?: string;
/** Populated for custom_ indicators; drives TV custom study auto-construction. */
custom_metadata?: CustomIndicatorMetadata;
}
/**