Skip to main content
Glama
nonnname

T-Invest MCP Server

by nonnname

post_order

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,
            };
          }
        },
      );
    }

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