Skip to main content
Glama
nonnname

T-Invest MCP Server

by nonnname

post_order

Destructive

Place market or limit orders for stocks in T-Invest accounts using a two-step confirmation process to verify trades before execution.

Instructions

Выставить биржевую заявку в Т-Инвестициях (требуется подтверждение: сначала вызовите без confirm, затем с confirm: true)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
accountIdYesИдентификатор счёта (можно получить через get_accounts)
tickerYesТикер инструмента
directionYesНаправление: buy — покупка, sell — продажа
quantityYesКоличество лотов (не более 10 000)
orderTypeYesТип заявки: market — рыночная, limit — лимитная
priceNoЦена для лимитной заявки (обязательна при orderType: limit)
confirmNoПередайте true для исполнения сделки. Без этого параметра возвращается только превью (если включено подтверждение).

Implementation Reference

  • The handler function that executes the order placement logic by calling the TInvestClient.
    async ({ accountId, ticker, direction, quantity, orderType, price, confirm }) => {
      try {
        if (orderType === 'limit' && price === undefined) {
          return {
            content: [{ type: 'text' as const, text: 'Для лимитной заявки необходимо указать price.' }],
            isError: true,
          };
        }
    
        const item = await resolveTickerToInstrument(client, ticker);
        if (!item) {
          return {
            content: [{ type: 'text' as const, text: `Инструмент "${ticker}" не найден.` }],
            isError: true,
          };
        }
    
        const priceStr = orderType === 'limit' && price !== undefined ? `${price}` : 'рыночная';
        const preview = [
          `Заявка:`,
          `  Тикер:       ${ticker}`,
          `  Направление: ${direction === 'buy' ? 'Покупка' : 'Продажа'}`,
          `  Тип:         ${orderType === 'limit' ? 'Лимитная' : 'Рыночная'}`,
          `  Количество:  ${quantity} лот(ов)`,
          `  Цена:        ${priceStr}`,
          `  Счёт:        ${accountId}`,
        ].join('\n');
    
        if (requireConfirmation && confirm !== true) {
          return {
            content: [{ type: 'text' as const, text: `${preview}\n\nДля исполнения вызовите инструмент повторно с параметром confirm: true` }],
          };
        }
    
        const body: Record<string, unknown> = {
          accountId,
          instrumentId: item.uid,
          quantity,
          direction: DIRECTION_MAP[direction],
          orderType: ORDER_TYPE_MAP[orderType],
          orderId: crypto.randomUUID(),
        };
    
        if (orderType === 'limit' && price !== undefined) {
          body.price = toQuotation(price);
        }
    
        const resp = await client.post<PostOrderResponse>(
          API_PATHS.ORDERS.POST_ORDER,
          body,
        );
    
        const status = ORDER_STATUS_LABELS[resp.executionReportStatus] ?? resp.executionReportStatus;
        const lines = [
          `Заявка выставлена`,
          `ID заявки: ${resp.orderId}`,
          `Статус: ${status}`,
          `Лотов запрошено / исполнено: ${resp.lotsRequested} / ${resp.lotsExecuted}`,
        ];
        if (resp.totalOrderAmount) lines.push(`Сумма: ${formatMoney(resp.totalOrderAmount)}`);
        if (resp.initialCommission) lines.push(`Комиссия: ${formatMoney(resp.initialCommission)}`);
        if (resp.message) lines.push(`Сообщение: ${resp.message}`);
    
        return { content: [{ type: 'text' as const, text: lines.join('\n') }] };
      } catch (error) {
        return {
          content: [{ type: 'text' as const, text: `Ошибка: ${error instanceof Error ? error.message : String(error)}` }],
          isError: true,
        };
      }
    },
  • Zod schema for validating the post_order tool inputs.
    {
      accountId: z.string().describe('Идентификатор счёта (можно получить через get_accounts)'),
      ticker: z.string().describe('Тикер инструмента'),
      direction: z.enum(['buy', 'sell']).describe('Направление: buy — покупка, sell — продажа'),
      quantity: z.number().int().min(1).max(10_000).describe('Количество лотов (не более 10 000)'),
      orderType: z.enum(['market', 'limit']).describe('Тип заявки: market — рыночная, limit — лимитная'),
      price: z
        .number()
        .positive()
        .optional()
        .describe('Цена для лимитной заявки (обязательна при orderType: limit)'),
      confirm: z
        .boolean()
        .optional()
        .describe('Передайте true для исполнения сделки. Без этого параметра возвращается только превью (если включено подтверждение).'),
    },
  • Registration function for the post_order tool within the McpServer.
    export function registerPostOrder(
      server: McpServer,
      client: TInvestClient,
      requireConfirmation: boolean,
    ): void {
      const description = requireConfirmation
        ? 'Выставить биржевую заявку в Т-Инвестициях (требуется подтверждение: сначала вызовите без confirm, затем с confirm: true)'
        : 'Выставить биржевую заявку в Т-Инвестициях';
    
      server.tool(
        'post_order',
        description,
        {
          accountId: z.string().describe('Идентификатор счёта (можно получить через get_accounts)'),
          ticker: z.string().describe('Тикер инструмента'),
          direction: z.enum(['buy', 'sell']).describe('Направление: buy — покупка, sell — продажа'),
          quantity: z.number().int().min(1).max(10_000).describe('Количество лотов (не более 10 000)'),
          orderType: z.enum(['market', 'limit']).describe('Тип заявки: market — рыночная, limit — лимитная'),
          price: z
            .number()
            .positive()
            .optional()
            .describe('Цена для лимитной заявки (обязательна при orderType: limit)'),
          confirm: z
            .boolean()
            .optional()
            .describe('Передайте true для исполнения сделки. Без этого параметра возвращается только превью (если включено подтверждение).'),
        },
        DESTRUCTIVE,
        async ({ accountId, ticker, direction, quantity, orderType, price, confirm }) => {
          try {
            if (orderType === 'limit' && price === undefined) {
              return {
                content: [{ type: 'text' as const, text: 'Для лимитной заявки необходимо указать price.' }],
                isError: true,
              };
            }
    
            const item = await resolveTickerToInstrument(client, ticker);
            if (!item) {
              return {
                content: [{ type: 'text' as const, text: `Инструмент "${ticker}" не найден.` }],
                isError: true,
              };
            }
    
            const priceStr = orderType === 'limit' && price !== undefined ? `${price}` : 'рыночная';
            const preview = [
              `Заявка:`,
              `  Тикер:       ${ticker}`,
              `  Направление: ${direction === 'buy' ? 'Покупка' : 'Продажа'}`,
              `  Тип:         ${orderType === 'limit' ? 'Лимитная' : 'Рыночная'}`,
              `  Количество:  ${quantity} лот(ов)`,
              `  Цена:        ${priceStr}`,
              `  Счёт:        ${accountId}`,
            ].join('\n');
    
            if (requireConfirmation && confirm !== true) {
              return {
                content: [{ type: 'text' as const, text: `${preview}\n\nДля исполнения вызовите инструмент повторно с параметром confirm: true` }],
              };
            }
    
            const body: Record<string, unknown> = {
              accountId,
              instrumentId: item.uid,
              quantity,
              direction: DIRECTION_MAP[direction],
              orderType: ORDER_TYPE_MAP[orderType],
              orderId: crypto.randomUUID(),
            };
    
            if (orderType === 'limit' && price !== undefined) {
              body.price = toQuotation(price);
            }
    
            const resp = await client.post<PostOrderResponse>(
              API_PATHS.ORDERS.POST_ORDER,
              body,
            );
    
            const status = ORDER_STATUS_LABELS[resp.executionReportStatus] ?? resp.executionReportStatus;
            const lines = [
              `Заявка выставлена`,
              `ID заявки: ${resp.orderId}`,
              `Статус: ${status}`,
              `Лотов запрошено / исполнено: ${resp.lotsRequested} / ${resp.lotsExecuted}`,
            ];
            if (resp.totalOrderAmount) lines.push(`Сумма: ${formatMoney(resp.totalOrderAmount)}`);
            if (resp.initialCommission) lines.push(`Комиссия: ${formatMoney(resp.initialCommission)}`);
            if (resp.message) lines.push(`Сообщение: ${resp.message}`);
    
            return { content: [{ type: 'text' as const, text: lines.join('\n') }] };
          } catch (error) {
            return {
              content: [{ type: 'text' as const, text: `Ошибка: ${error instanceof Error ? error.message : String(error)}` }],
              isError: true,
            };
          }
        },
      );
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

While annotations declare destructiveHint=true, the description adds crucial behavioral context: the two-phase execution model (preview mode vs actual execution), what happens without confirm (returns preview only), and the confirmation requirement. This safety-critical workflow detail is valuable behavioral disclosure beyond the structured annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Every element earns its place: the main clause defines the action/resource, while the parenthetical efficiently packs the critical confirmation workflow instruction. No redundancy or tautology. The Russian phrasing is appropriately dense for a financial API operation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the 100% schema coverage and presence of destructive annotations, the description adequately covers the critical confirmation complexity unique to this trading tool. It explains the preview-vs-execution behavior well. Minor gap: no mention of output structure (though none defined) or specific error conditions/trading restrictions that might occur.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 100% schema description coverage, the baseline is 3. The description adds significant semantic value specifically for the 'confirm' parameter, explaining the two-step execution workflow (preview then confirm) that isn't fully captured by the schema's brief description. However, it adds minimal context for other well-documented parameters like ticker or direction.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific action verb ('Выставить'/'Place') and clearly identifies the resource (exchange order/'биржевую заявку') and scope (T-Investments). It effectively distinguishes from sibling tools like cancel_order and get_orders by specifying this creates new orders, and implicitly from post_stop_order by specifying 'exchange order' (биржевую заявка) versus stop orders.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides explicit, critical usage context in the parenthetical: the confirmation workflow requires calling first without confirm, then with confirm:true. This is a clear 'when-to-use' pattern for the confirm parameter states. However, it lacks explicit comparison to sibling alternatives (e.g., distinguishing from post_stop_order) or prerequisite mentions (though schema references get_accounts).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nonnname/t-invest-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server