Skip to main content
Glama

get-interline-tickets

Find available interline train tickets on China's 12306 railway system by specifying departure, destination, and optional transfer stations. This tool displays up to 10 results for planning multi-leg journeys.

Instructions

查询12306中转余票信息。尚且只支持查询前十条。

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dateYes查询日期,格式为 "yyyy-MM-dd"。如果用户提供的是相对日期(如“明天”),请务必先调用 `get-current-date` 接口获取当前日期,并计算出目标日期。
fromStationYes出发地的 `station_code` 。必须是通过 `get-station-code-by-name` 或 `get-station-code-of-city` 接口查询得到的编码,严禁直接使用中文地名。
toStationYes出发地的 `station_code` 。必须是通过 `get-station-code-by-name` 或 `get-station-code-of-city` 接口查询得到的编码,严禁直接使用中文地名。
middleStationNo中转地的 `station_code` ,可选。必须是通过 `get-station-code-by-name` 或 `get-station-code-of-city` 接口查询得到的编码,严禁直接使用中文地名。
showWZNo是否显示无座车,默认不显示无座车。
trainFilterFlagsNo车次筛选条件,默认为空。从以下标志中选取多个条件组合[G(高铁/城际),D(动车),Z(直达特快),T(特快),K(快速),O(其他),F(复兴号),S(智能动车组)]

Implementation Reference

  • The main handler function for 'get-interline-tickets' tool. It validates input, fetches data from 12306 interline API (/lcquery/queryG), handles errors, parses the response using parseInterlinesInfo, applies train filters, and formats the output using formatInterlinesInfo.
    async ({
      date,
      fromStation,
      toStation,
      middleStation,
      showWZ,
      trainFilterFlags,
    }) => {
      // 检查日期是否早于当前日期
      if (!checkDate(date)) {
        return {
          content: [
            {
              type: 'text',
              text: 'Error: The date cannot be earlier than today.',
            },
          ],
        };
      }
      if (
        !Object.keys(STATIONS).includes(fromStation) ||
        !Object.keys(STATIONS).includes(toStation)
      ) {
        return {
          content: [{ type: 'text', text: 'Error: Station not found. ' }],
        };
      }
      const queryUrl = `${API_BASE}/lcquery/queryG`;
      const queryParams = new URLSearchParams({
        train_date: date,
        from_station_telecode: fromStation,
        to_station_telecode: toStation,
        middle_station: middleStation,
        result_index: '0',
        can_query: 'Y',
        isShowWZ: showWZ ? 'Y' : 'N',
        purpose_codes: '00', // 00: 成人票 0X: 学生票
        channel: 'E', // 没搞清楚什么用
      });
      const cookies = await getCookie(API_BASE);
      if (cookies == null) {
        return {
          content: [
            {
              type: 'text',
              text: 'Error: get cookie failed. Check your network.',
            },
          ],
        };
      }
      const queryResponse = await make12306Request<InterlineQueryResponse>(
        queryUrl,
        queryParams,
        { Cookie: formatCookies(cookies) }
      );
      // 处理请求错误
      if (queryResponse === null || queryResponse === undefined) {
        return {
          content: [
            {
              type: 'text',
              text: 'Error: request interline tickets data failed. ',
            },
          ],
        };
      }
      // 请求成功,但查询有误
      if (typeof queryResponse.data == 'string') {
        return {
          content: [{ type: 'text', text: queryResponse.errorMsg }],
        };
      }
      // 请求和查询都没问题
      let interlineTicketsInfo: InterlineInfo[];
      try {
        interlineTicketsInfo = parseInterlinesInfo(queryResponse.data.middleList);
      } catch (error) {
        return {
          content: [
            { type: 'text', text: `Error: parse tickets info failed. ${error}` },
          ],
        };
      }
      const filteredInterlineTicketsInfo = filterTicketsInfo<InterlineInfo>(
        interlineTicketsInfo,
        trainFilterFlags
      );
      return {
        content: [
          {
            type: 'text',
            text: formatInterlinesInfo(filteredInterlineTicketsInfo),
          },
        ],
      };
    }
  • Zod schema defining the input parameters for the 'get-interline-tickets' tool, including date, stations, middle station, showWZ flag, and train filters.
      date: z
        .string()
        .length(10)
        .describe(
          '查询日期,格式为 "yyyy-MM-dd"。如果用户提供的是相对日期(如“明天”),请务必先调用 `get-current-date` 接口获取当前日期,并计算出目标日期。'
        ),
      fromStation: z
        .string()
        .describe(
          '出发地的 `station_code` 。必须是通过 `get-station-code-by-name` 或 `get-station-code-of-city` 接口查询得到的编码,严禁直接使用中文地名。'
        ),
      toStation: z
        .string()
        .describe(
          '出发地的 `station_code` 。必须是通过 `get-station-code-by-name` 或 `get-station-code-of-city` 接口查询得到的编码,严禁直接使用中文地名。'
        ),
      middleStation: z
        .string()
        .optional()
        .default('')
        .describe(
          '中转地的 `station_code` ,可选。必须是通过 `get-station-code-by-name` 或 `get-station-code-of-city` 接口查询得到的编码,严禁直接使用中文地名。'
        ),
      showWZ: z
        .boolean()
        .optional()
        .default(false)
        .describe('是否显示无座车,默认不显示无座车。'),
      trainFilterFlags: z
        .string()
        .regex(/^[GDZTKOFS]*$/)
        .max(8)
        .optional()
        .default('')
        .describe(
          '车次筛选条件,默认为空。从以下标志中选取多个条件组合[G(高铁/城际),D(动车),Z(直达特快),T(特快),K(快速),O(其他),F(复兴号),S(智能动车组)]'
        ),
    },
  • src/index.ts:809-947 (registration)
    Registration of the 'get-interline-tickets' tool using server.tool, including name, description, input schema, and handler function.
    server.tool(
      'get-interline-tickets',
      '查询12306中转余票信息。尚且只支持查询前十条。',
      {
        date: z
          .string()
          .length(10)
          .describe(
            '查询日期,格式为 "yyyy-MM-dd"。如果用户提供的是相对日期(如“明天”),请务必先调用 `get-current-date` 接口获取当前日期,并计算出目标日期。'
          ),
        fromStation: z
          .string()
          .describe(
            '出发地的 `station_code` 。必须是通过 `get-station-code-by-name` 或 `get-station-code-of-city` 接口查询得到的编码,严禁直接使用中文地名。'
          ),
        toStation: z
          .string()
          .describe(
            '出发地的 `station_code` 。必须是通过 `get-station-code-by-name` 或 `get-station-code-of-city` 接口查询得到的编码,严禁直接使用中文地名。'
          ),
        middleStation: z
          .string()
          .optional()
          .default('')
          .describe(
            '中转地的 `station_code` ,可选。必须是通过 `get-station-code-by-name` 或 `get-station-code-of-city` 接口查询得到的编码,严禁直接使用中文地名。'
          ),
        showWZ: z
          .boolean()
          .optional()
          .default(false)
          .describe('是否显示无座车,默认不显示无座车。'),
        trainFilterFlags: z
          .string()
          .regex(/^[GDZTKOFS]*$/)
          .max(8)
          .optional()
          .default('')
          .describe(
            '车次筛选条件,默认为空。从以下标志中选取多个条件组合[G(高铁/城际),D(动车),Z(直达特快),T(特快),K(快速),O(其他),F(复兴号),S(智能动车组)]'
          ),
      },
      async ({
        date,
        fromStation,
        toStation,
        middleStation,
        showWZ,
        trainFilterFlags,
      }) => {
        // 检查日期是否早于当前日期
        if (!checkDate(date)) {
          return {
            content: [
              {
                type: 'text',
                text: 'Error: The date cannot be earlier than today.',
              },
            ],
          };
        }
        if (
          !Object.keys(STATIONS).includes(fromStation) ||
          !Object.keys(STATIONS).includes(toStation)
        ) {
          return {
            content: [{ type: 'text', text: 'Error: Station not found. ' }],
          };
        }
        const queryUrl = `${API_BASE}/lcquery/queryG`;
        const queryParams = new URLSearchParams({
          train_date: date,
          from_station_telecode: fromStation,
          to_station_telecode: toStation,
          middle_station: middleStation,
          result_index: '0',
          can_query: 'Y',
          isShowWZ: showWZ ? 'Y' : 'N',
          purpose_codes: '00', // 00: 成人票 0X: 学生票
          channel: 'E', // 没搞清楚什么用
        });
        const cookies = await getCookie(API_BASE);
        if (cookies == null) {
          return {
            content: [
              {
                type: 'text',
                text: 'Error: get cookie failed. Check your network.',
              },
            ],
          };
        }
        const queryResponse = await make12306Request<InterlineQueryResponse>(
          queryUrl,
          queryParams,
          { Cookie: formatCookies(cookies) }
        );
        // 处理请求错误
        if (queryResponse === null || queryResponse === undefined) {
          return {
            content: [
              {
                type: 'text',
                text: 'Error: request interline tickets data failed. ',
              },
            ],
          };
        }
        // 请求成功,但查询有误
        if (typeof queryResponse.data == 'string') {
          return {
            content: [{ type: 'text', text: queryResponse.errorMsg }],
          };
        }
        // 请求和查询都没问题
        let interlineTicketsInfo: InterlineInfo[];
        try {
          interlineTicketsInfo = parseInterlinesInfo(queryResponse.data.middleList);
        } catch (error) {
          return {
            content: [
              { type: 'text', text: `Error: parse tickets info failed. ${error}` },
            ],
          };
        }
        const filteredInterlineTicketsInfo = filterTicketsInfo<InterlineInfo>(
          interlineTicketsInfo,
          trainFilterFlags
        );
        return {
          content: [
            {
              type: 'text',
              text: formatInterlinesInfo(filteredInterlineTicketsInfo),
            },
          ],
        };
      }
    );
  • Helper function to parse raw InterlineData from API into structured InterlineInfo array, used in the handler.
    function parseInterlinesInfo(interlineData: InterlineData[]): InterlineInfo[] {
      const result: InterlineInfo[] = [];
      for (const ticket of interlineData) {
        const interlineTickets = parseInterlinesTicketInfo(ticket.fullList);
        result.push({
          all_lishi: ticket.all_lishi,
          start_time: ticket.start_time,
          start_date: ticket.train_date,
          middle_date: ticket.middle_date,
          arrive_date: ticket.arrive_date,
          arrive_time: ticket.arrive_time,
          from_station_code: ticket.from_station_code,
          from_station_name: ticket.from_station_name,
          middle_station_code: ticket.middle_station_code,
          middle_station_name: ticket.middle_station_name,
          end_station_code: ticket.end_station_code,
          end_station_name: ticket.end_station_name,
          start_train_code: interlineTickets[0].start_train_code,
          first_train_no: ticket.first_train_no,
          second_train_no: ticket.second_train_no,
          train_count: ticket.train_count,
          ticketList: interlineTickets,
          same_station: ticket.same_station == '0' ? true : false,
          same_train: ticket.same_train == 'Y' ? true : false,
          wait_time: ticket.wait_time,
        });
      }
      return result;
    }
  • Helper function to format InterlineInfo array into a readable text summary for the tool response.
    function formatInterlinesInfo(interlinesInfo: InterlineInfo[]): string {
      let result =
        '出发时间 -> 到达时间 | 出发车站 -> 中转车站 -> 到达车站 | 换乘标志 |换乘等待时间| 总历时\n\n';
      interlinesInfo.forEach((interlineInfo) => {
        result += `${interlineInfo.start_date} ${interlineInfo.start_time} -> ${interlineInfo.arrive_date} ${interlineInfo.arrive_time} | `;
        result += `${interlineInfo.from_station_name} -> ${interlineInfo.middle_station_name} -> ${interlineInfo.end_station_name} | `;
        result += `${
          interlineInfo.same_train
            ? '同车换乘'
            : interlineInfo.same_station
            ? '同站换乘'
            : '换站换乘'
        } | ${interlineInfo.wait_time} | ${interlineInfo.all_lishi}\n\n`;
        result +=
          '\t' + formatTicketsInfo(interlineInfo.ticketList).replace(/\n/g, '\n\t');
        result += '\n';
      });
      return result;
    }
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions a limitation ('只支持查询前十条' - only supports querying the first ten results), which is useful, but fails to describe other critical behaviors such as response format, error handling, authentication needs, or rate limits. For a query tool with six parameters, this leaves significant gaps.

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

Conciseness4/5

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

The description is concise with two sentences: one stating the purpose and one noting a limitation. It is front-loaded with the core function and avoids unnecessary details, though it could be slightly more structured for clarity.

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

Completeness2/5

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

Given the complexity (6 parameters, no annotations, no output schema), the description is incomplete. It lacks information on output format, error conditions, and how results are structured, which is critical for an AI agent to use this tool effectively. The limitation mentioned is helpful but insufficient for full contextual understanding.

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

Parameters3/5

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

Schema description coverage is 100%, meaning all parameters are well-documented in the input schema itself. The description adds no additional parameter semantics beyond what the schema provides, so it meets the baseline of 3 without compensating or adding extra value.

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

Purpose4/5

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

The description clearly states the verb ('查询' meaning 'query') and resource ('12306中转余票信息' meaning '12306 interline ticket availability information'), making the purpose specific and understandable. However, it doesn't explicitly differentiate from sibling tools like 'get-tickets' (which likely queries direct tickets), leaving room for ambiguity.

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

Usage Guidelines3/5

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

The description implies usage by stating it '只支持查询前十条' (only supports querying the first ten results), which suggests a limitation on result scope. However, it lacks explicit guidance on when to use this tool versus alternatives like 'get-tickets' or how it relates to sibling tools, relying on implicit context.

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/freestylefly/12306-mcp'

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