fix
This commit is contained in:
@@ -12,6 +12,18 @@ export type ServerConfig = {
|
||||
authBaseUrl?: string
|
||||
mtsBaseUrl?: string
|
||||
izanagiBaseUrl?: string
|
||||
foreignStockBaseUrl?: string
|
||||
usStockBaseUrl?: string
|
||||
foreignStockRestUrl?: string
|
||||
foreignStockGraphqlBffUrl?: string
|
||||
foreignStockGraphqlIntUrl?: string
|
||||
mainSiteBaseUrl?: string
|
||||
mainSiteEtGatePath?: string
|
||||
mainSiteAssetsValuationsPath?: string
|
||||
mainSiteExchangeOrderInputPath?: string
|
||||
mainSiteExchangeOrderPasswordPath?: string
|
||||
mainSiteExchangeOrderConfirmPath?: string
|
||||
mainSiteExchangeOrderCompletePath?: string
|
||||
}
|
||||
|
||||
const optionalUrl = (value: string | undefined) => {
|
||||
@@ -38,5 +50,17 @@ export const loadConfig = (): ServerConfig => {
|
||||
authBaseUrl: optionalUrl(process.env.SBI_AUTH_BASE_URL),
|
||||
mtsBaseUrl: optionalUrl(process.env.SBI_MTS_BASE_URL),
|
||||
izanagiBaseUrl: optionalUrl(process.env.SBI_IZANAGI_BASE_URL),
|
||||
foreignStockBaseUrl: optionalUrl(process.env.SBI_FOREIGN_STOCK_BASE_URL),
|
||||
usStockBaseUrl: optionalUrl(process.env.SBI_US_STOCK_BASE_URL),
|
||||
foreignStockRestUrl: optionalUrl(process.env.SBI_FOREIGN_STOCK_REST_URL),
|
||||
foreignStockGraphqlBffUrl: optionalUrl(process.env.SBI_FOREIGN_STOCK_GRAPHQL_BFF_URL),
|
||||
foreignStockGraphqlIntUrl: optionalUrl(process.env.SBI_FOREIGN_STOCK_GRAPHQL_INT_URL),
|
||||
mainSiteBaseUrl: optionalUrl(process.env.SBI_MAIN_SITE_BASE_URL),
|
||||
mainSiteEtGatePath: process.env.SBI_MAIN_SITE_ET_GATE_PATH,
|
||||
mainSiteAssetsValuationsPath: process.env.SBI_MAIN_SITE_ASSETS_VALUATIONS_PATH,
|
||||
mainSiteExchangeOrderInputPath: process.env.SBI_MAIN_SITE_EXCHANGE_ORDER_INPUT_PATH,
|
||||
mainSiteExchangeOrderPasswordPath: process.env.SBI_MAIN_SITE_EXCHANGE_ORDER_PASSWORD_PATH,
|
||||
mainSiteExchangeOrderConfirmPath: process.env.SBI_MAIN_SITE_EXCHANGE_ORDER_CONFIRM_PATH,
|
||||
mainSiteExchangeOrderCompletePath: process.env.SBI_MAIN_SITE_EXCHANGE_ORDER_COMPLETE_PATH,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ const orderSubmitMethodByEstimateMethod = {
|
||||
'orders.ifd.estimateCorrection': 'orders.ifd.placeCorrection',
|
||||
'orders.ifd.estimateCancel': 'orders.ifd.placeCancel',
|
||||
'orders.themeInvestment.estimate': 'orders.themeInvestment.place',
|
||||
'orders.exchange.estimate': 'orders.exchange.place',
|
||||
} as const satisfies Partial<Record<RpcMethod, RpcMethod>>
|
||||
|
||||
const submitMethodForEstimateMethod = (method: RpcMethod) =>
|
||||
@@ -567,9 +568,39 @@ const placeThemeInvestmentOrderSchema = themeInvestmentOrderSchema.extend({
|
||||
.describe('Explicitly allows sending a live theme investment order'),
|
||||
})
|
||||
|
||||
const exchangeOrderSchema = z.object({
|
||||
currencyCode: z.string().min(1).describe('Currency code, such as USD'),
|
||||
side: z.enum(['buy', 'sell']).describe('Buy or sell the foreign currency'),
|
||||
tradeQuantity: z.union([z.number().positive(), z.string().min(1)]).describe('Order quantity'),
|
||||
specificMethod: z
|
||||
.enum(['foreign', 'domestic'])
|
||||
.optional()
|
||||
.describe('foreign for foreign-currency quantity, domestic for yen amount'),
|
||||
accountKind: z.enum(['GENERAL', 'JR_NISA']).optional().describe('SBI exchange account kind'),
|
||||
sellMethod: z.enum(['SELL_PART', 'SELL_ALL']).optional().describe('Required for sell orders'),
|
||||
orderAmount: z
|
||||
.union([z.number().positive(), z.string().min(1)])
|
||||
.optional()
|
||||
.describe('Hidden order amount posted to SBI; required for domestic orders'),
|
||||
tradePassword: z.string().optional().describe('Trading password used by SBI'),
|
||||
})
|
||||
|
||||
const exchangeRateSchema = exchangeOrderSchema.pick({
|
||||
currencyCode: true,
|
||||
side: true,
|
||||
})
|
||||
|
||||
const placeExchangeOrderSchema = exchangeOrderSchema.extend({
|
||||
allowTrading: z
|
||||
.literal(true)
|
||||
.optional()
|
||||
.describe('Explicitly allows sending a live exchange order'),
|
||||
})
|
||||
|
||||
const methodParamSchemas = {
|
||||
'session.profile': undefined,
|
||||
'account.profile': undefined,
|
||||
'account.assets.current': undefined,
|
||||
'account.power.buyingPower': undefined,
|
||||
'account.power.collateralRatio': undefined,
|
||||
'account.positions.cash': cashPositionOptionsSchema.optional(),
|
||||
@@ -629,6 +660,9 @@ const methodParamSchemas = {
|
||||
'orders.themeInvestment.list': themeInvestmentPreOrderSchema,
|
||||
'orders.themeInvestment.estimate': themeInvestmentOrderSchema,
|
||||
'orders.themeInvestment.place': placeThemeInvestmentOrderSchema,
|
||||
'orders.exchange.rate': exchangeRateSchema,
|
||||
'orders.exchange.estimate': exchangeOrderSchema,
|
||||
'orders.exchange.place': placeExchangeOrderSchema,
|
||||
} satisfies Record<RpcMethod, z.ZodType | undefined>
|
||||
|
||||
const createMcpServer = (c: Context<AppBindings>) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { SbiClientMethods } from '@repo/sbi-client'
|
||||
export const RPC_METHODS = [
|
||||
'session.profile',
|
||||
'account.profile',
|
||||
'account.assets.current',
|
||||
'account.power.buyingPower',
|
||||
'account.power.collateralRatio',
|
||||
'account.positions.cash',
|
||||
@@ -62,6 +63,9 @@ export const RPC_METHODS = [
|
||||
'orders.themeInvestment.list',
|
||||
'orders.themeInvestment.estimate',
|
||||
'orders.themeInvestment.place',
|
||||
'orders.exchange.rate',
|
||||
'orders.exchange.estimate',
|
||||
'orders.exchange.place',
|
||||
] as const
|
||||
|
||||
export type RpcMethod = (typeof RPC_METHODS)[number]
|
||||
@@ -80,6 +84,7 @@ const tradingMethods = new Set<string>([
|
||||
'orders.ifd.placeCorrection',
|
||||
'orders.ifd.placeCancel',
|
||||
'orders.themeInvestment.place',
|
||||
'orders.exchange.place',
|
||||
])
|
||||
|
||||
const cashOrderMethods = new Set<string>([
|
||||
|
||||
@@ -27,6 +27,18 @@ export const connectSbi = async (
|
||||
authBaseUrl: config.authBaseUrl,
|
||||
mtsBaseUrl: config.mtsBaseUrl,
|
||||
izanagiBaseUrl: config.izanagiBaseUrl,
|
||||
foreignStockBaseUrl: config.foreignStockBaseUrl,
|
||||
usStockBaseUrl: config.usStockBaseUrl,
|
||||
foreignStockRestUrl: config.foreignStockRestUrl,
|
||||
foreignStockGraphqlBffUrl: config.foreignStockGraphqlBffUrl,
|
||||
foreignStockGraphqlIntUrl: config.foreignStockGraphqlIntUrl,
|
||||
mainSiteBaseUrl: config.mainSiteBaseUrl,
|
||||
mainSiteEtGatePath: config.mainSiteEtGatePath,
|
||||
mainSiteAssetsValuationsPath: config.mainSiteAssetsValuationsPath,
|
||||
mainSiteExchangeOrderInputPath: config.mainSiteExchangeOrderInputPath,
|
||||
mainSiteExchangeOrderPasswordPath: config.mainSiteExchangeOrderPasswordPath,
|
||||
mainSiteExchangeOrderConfirmPath: config.mainSiteExchangeOrderConfirmPath,
|
||||
mainSiteExchangeOrderCompletePath: config.mainSiteExchangeOrderCompletePath,
|
||||
},
|
||||
clientOptions,
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SbiClientMethods } from '@repo/sbi-client'
|
||||
import type { MarketCode, SbiClientMethods } from '@repo/sbi-client'
|
||||
import { createBunWebSocket } from 'hono/bun'
|
||||
import type { WSContext } from 'hono/ws'
|
||||
import { randomUUID } from 'node:crypto'
|
||||
@@ -27,7 +27,7 @@ type RpcSocketState = {
|
||||
|
||||
type BoardPollingParams = {
|
||||
issueCode: string
|
||||
market?: string
|
||||
market: MarketCode
|
||||
intervalSeconds?: number
|
||||
}
|
||||
|
||||
@@ -67,8 +67,11 @@ const parseBoardPollingParams = (params: unknown): BoardPollingParams => {
|
||||
if (typeof value.issueCode !== 'string' || !value.issueCode) {
|
||||
throw new Error('issueCode is required')
|
||||
}
|
||||
if (value.market != null && typeof value.market !== 'string') {
|
||||
throw new Error('market must be a string')
|
||||
if (typeof value.market !== 'string' || !value.market) {
|
||||
throw new Error('market is required')
|
||||
}
|
||||
if (!['XTKS', 'XNAS', 'XNYS', 'ARCX'].includes(value.market)) {
|
||||
throw new Error('market must be XTKS, XNAS, XNYS, or ARCX')
|
||||
}
|
||||
if (
|
||||
value.intervalSeconds != null &&
|
||||
@@ -81,7 +84,7 @@ const parseBoardPollingParams = (params: unknown): BoardPollingParams => {
|
||||
|
||||
return {
|
||||
issueCode: value.issueCode,
|
||||
market: typeof value.market === 'string' ? value.market : undefined,
|
||||
market: value.market as MarketCode,
|
||||
intervalSeconds: typeof value.intervalSeconds === 'number' ? value.intervalSeconds : undefined,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user