Add four new example servers demonstrating different SEP-2053 variant patterns: - model-optimized (same tools, different descriptions per LLM family) - trading (API versioning with deprecation lifecycle) - research (context budget management) - Refactor the existing variants-http example into a GitHub-style server with custom ranking. Signed-off-by: Kurt Degiorgio <kdegiorgio@bloomberg.net>
296 lines
9.1 KiB
Go
296 lines
9.1 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
// -- V2 tools -----------------------------------------------------------------
|
|
|
|
type PlaceOrderInput struct {
|
|
Symbol string `json:"symbol" jsonschema:"ticker symbol (e.g. AAPL)"`
|
|
Side string `json:"side" jsonschema:"order side: buy, sell"`
|
|
Quantity int `json:"quantity" jsonschema:"number of shares"`
|
|
Type string `json:"type,omitempty" jsonschema:"order type: market, limit (default: market)"`
|
|
Price float64 `json:"price,omitempty" jsonschema:"limit price (required for limit orders)"`
|
|
}
|
|
|
|
type PlaceOrderOutput struct {
|
|
OrderID string `json:"orderId"`
|
|
Symbol string `json:"symbol"`
|
|
Side string `json:"side"`
|
|
Status string `json:"status"`
|
|
Price float64 `json:"filledPrice"`
|
|
}
|
|
|
|
func placeOrder(_ context.Context, _ *mcp.CallToolRequest, in PlaceOrderInput) (*mcp.CallToolResult, PlaceOrderOutput, error) {
|
|
price := in.Price
|
|
if price == 0 {
|
|
price = 185.50
|
|
}
|
|
return nil, PlaceOrderOutput{
|
|
OrderID: "ORD-20250115-001",
|
|
Symbol: in.Symbol,
|
|
Side: in.Side,
|
|
Status: "filled",
|
|
Price: price,
|
|
}, nil
|
|
}
|
|
|
|
type GetQuotesInput struct {
|
|
Symbols []string `json:"symbols" jsonschema:"list of ticker symbols"`
|
|
}
|
|
|
|
type Quote struct {
|
|
Symbol string `json:"symbol"`
|
|
Bid float64 `json:"bid"`
|
|
Ask float64 `json:"ask"`
|
|
Last float64 `json:"last"`
|
|
Volume int `json:"volume"`
|
|
}
|
|
|
|
type GetQuotesOutput struct {
|
|
Quotes []Quote `json:"quotes"`
|
|
}
|
|
|
|
func getQuotes(_ context.Context, _ *mcp.CallToolRequest, in GetQuotesInput) (*mcp.CallToolResult, GetQuotesOutput, error) {
|
|
quotes := make([]Quote, len(in.Symbols))
|
|
for i, sym := range in.Symbols {
|
|
quotes[i] = Quote{
|
|
Symbol: sym,
|
|
Bid: 185.40 + float64(i),
|
|
Ask: 185.60 + float64(i),
|
|
Last: 185.50 + float64(i),
|
|
Volume: 1500000 + i*100000,
|
|
}
|
|
}
|
|
return nil, GetQuotesOutput{Quotes: quotes}, nil
|
|
}
|
|
|
|
type GetPortfolioInput struct {
|
|
AccountID string `json:"accountId,omitempty" jsonschema:"account identifier (default: primary account)"`
|
|
}
|
|
|
|
type Position struct {
|
|
Symbol string `json:"symbol"`
|
|
Quantity int `json:"quantity"`
|
|
AvgCost float64 `json:"avgCost"`
|
|
Current float64 `json:"currentPrice"`
|
|
PnL float64 `json:"unrealizedPnL"`
|
|
}
|
|
|
|
type GetPortfolioOutput struct {
|
|
AccountID string `json:"accountId"`
|
|
Positions []Position `json:"positions"`
|
|
CashBal float64 `json:"cashBalance"`
|
|
TotalValue float64 `json:"totalValue"`
|
|
}
|
|
|
|
func getPortfolio(_ context.Context, _ *mcp.CallToolRequest, in GetPortfolioInput) (*mcp.CallToolResult, GetPortfolioOutput, error) {
|
|
return nil, GetPortfolioOutput{
|
|
AccountID: "ACCT-001",
|
|
Positions: []Position{
|
|
{Symbol: "AAPL", Quantity: 100, AvgCost: 175.00, Current: 185.50, PnL: 1050.00},
|
|
{Symbol: "GOOGL", Quantity: 50, AvgCost: 140.00, Current: 178.25, PnL: 1912.50},
|
|
},
|
|
CashBal: 25000.00,
|
|
TotalValue: 52337.50,
|
|
}, nil
|
|
}
|
|
|
|
type CancelOrderInput struct {
|
|
OrderID string `json:"orderId" jsonschema:"order identifier to cancel"`
|
|
}
|
|
|
|
type CancelOrderOutput struct {
|
|
OrderID string `json:"orderId"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
func cancelOrder(_ context.Context, _ *mcp.CallToolRequest, in CancelOrderInput) (*mcp.CallToolResult, CancelOrderOutput, error) {
|
|
return nil, CancelOrderOutput{
|
|
OrderID: in.OrderID,
|
|
Status: "cancelled",
|
|
}, nil
|
|
}
|
|
|
|
// -- V3 tools -----------------------------------------------------------------
|
|
|
|
type PlaceOrderV3Input struct {
|
|
Symbol string `json:"symbol" jsonschema:"ticker symbol"`
|
|
Side string `json:"side" jsonschema:"order side: buy, sell, short"`
|
|
Quantity int `json:"quantity" jsonschema:"number of shares"`
|
|
Type string `json:"type,omitempty" jsonschema:"order type: market, limit, stop, stop_limit (default: market)"`
|
|
Price float64 `json:"price,omitempty" jsonschema:"limit price"`
|
|
StopPrice float64 `json:"stopPrice,omitempty" jsonschema:"stop trigger price"`
|
|
TimeInForce string `json:"timeInForce,omitempty" jsonschema:"GTC, DAY, IOC, FOK (default: DAY)"`
|
|
Tags map[string]string `json:"tags,omitempty" jsonschema:"custom order tags for tracking"`
|
|
}
|
|
|
|
type PlaceOrderV3Output struct {
|
|
OrderID string `json:"orderId"`
|
|
Symbol string `json:"symbol"`
|
|
Side string `json:"side"`
|
|
Status string `json:"status"`
|
|
Price float64 `json:"filledPrice"`
|
|
TimeInForce string `json:"timeInForce"`
|
|
}
|
|
|
|
func placeOrderV3(_ context.Context, _ *mcp.CallToolRequest, in PlaceOrderV3Input) (*mcp.CallToolResult, PlaceOrderV3Output, error) {
|
|
price := in.Price
|
|
if price == 0 {
|
|
price = 185.50
|
|
}
|
|
tif := in.TimeInForce
|
|
if tif == "" {
|
|
tif = "DAY"
|
|
}
|
|
return nil, PlaceOrderV3Output{
|
|
OrderID: "ORD-V3-20250115-001",
|
|
Symbol: in.Symbol,
|
|
Side: in.Side,
|
|
Status: "filled",
|
|
Price: price,
|
|
TimeInForce: tif,
|
|
}, nil
|
|
}
|
|
|
|
type StreamQuotesInput struct {
|
|
Symbols []string `json:"symbols" jsonschema:"list of ticker symbols to stream"`
|
|
Interval int `json:"interval,omitempty" jsonschema:"update interval in milliseconds (default: 1000)"`
|
|
}
|
|
|
|
type StreamQuotesOutput struct {
|
|
Snapshot []Quote `json:"snapshot"`
|
|
StreamID string `json:"streamId"`
|
|
}
|
|
|
|
func streamQuotes(_ context.Context, _ *mcp.CallToolRequest, in StreamQuotesInput) (*mcp.CallToolResult, StreamQuotesOutput, error) {
|
|
quotes := make([]Quote, len(in.Symbols))
|
|
for i, sym := range in.Symbols {
|
|
quotes[i] = Quote{
|
|
Symbol: sym,
|
|
Bid: 185.40 + float64(i),
|
|
Ask: 185.60 + float64(i),
|
|
Last: 185.50 + float64(i),
|
|
Volume: 1500000 + i*100000,
|
|
}
|
|
}
|
|
return nil, StreamQuotesOutput{
|
|
Snapshot: quotes,
|
|
StreamID: "stream-001",
|
|
}, nil
|
|
}
|
|
|
|
type GetPortfolioV3Input struct {
|
|
AccountID string `json:"accountId,omitempty" jsonschema:"account identifier (default: primary account)"`
|
|
IncludeGreek bool `json:"includeGreeks,omitempty" jsonschema:"include options greeks in positions"`
|
|
}
|
|
|
|
type GetPortfolioV3Output struct {
|
|
AccountID string `json:"accountId"`
|
|
Positions []Position `json:"positions"`
|
|
CashBal float64 `json:"cashBalance"`
|
|
TotalValue float64 `json:"totalValue"`
|
|
MarginUsed float64 `json:"marginUsed"`
|
|
}
|
|
|
|
func getPortfolioV3(_ context.Context, _ *mcp.CallToolRequest, in GetPortfolioV3Input) (*mcp.CallToolResult, GetPortfolioV3Output, error) {
|
|
return nil, GetPortfolioV3Output{
|
|
AccountID: "ACCT-001",
|
|
Positions: []Position{
|
|
{Symbol: "AAPL", Quantity: 100, AvgCost: 175.00, Current: 185.50, PnL: 1050.00},
|
|
{Symbol: "GOOGL", Quantity: 50, AvgCost: 140.00, Current: 178.25, PnL: 1912.50},
|
|
},
|
|
CashBal: 25000.00,
|
|
TotalValue: 52337.50,
|
|
MarginUsed: 5000.00,
|
|
}, nil
|
|
}
|
|
|
|
// -- V1 legacy tools ----------------------------------------------------------
|
|
|
|
type TradeInput struct {
|
|
Ticker string `json:"ticker" jsonschema:"stock ticker"`
|
|
Action string `json:"action" jsonschema:"buy or sell"`
|
|
Shares int `json:"shares" jsonschema:"number of shares"`
|
|
}
|
|
|
|
type TradeOutput struct {
|
|
TradeID string `json:"tradeId"`
|
|
Status string `json:"status"`
|
|
}
|
|
|
|
func trade(_ context.Context, _ *mcp.CallToolRequest, in TradeInput) (*mcp.CallToolResult, TradeOutput, error) {
|
|
return nil, TradeOutput{
|
|
TradeID: fmt.Sprintf("T-%s-%d", in.Ticker, in.Shares),
|
|
Status: "executed",
|
|
}, nil
|
|
}
|
|
|
|
type QuoteInput struct {
|
|
Ticker string `json:"ticker" jsonschema:"stock ticker"`
|
|
}
|
|
|
|
type QuoteOutput struct {
|
|
Ticker string `json:"ticker"`
|
|
Price float64 `json:"price"`
|
|
}
|
|
|
|
func quote(_ context.Context, _ *mcp.CallToolRequest, in QuoteInput) (*mcp.CallToolResult, QuoteOutput, error) {
|
|
return nil, QuoteOutput{
|
|
Ticker: in.Ticker,
|
|
Price: 185.50,
|
|
}, nil
|
|
}
|
|
|
|
type BalanceInput struct {
|
|
Account string `json:"account,omitempty" jsonschema:"account name"`
|
|
}
|
|
|
|
type BalanceOutput struct {
|
|
Cash float64 `json:"cash"`
|
|
Total float64 `json:"total"`
|
|
}
|
|
|
|
func balance(_ context.Context, _ *mcp.CallToolRequest, in BalanceInput) (*mcp.CallToolResult, BalanceOutput, error) {
|
|
return nil, BalanceOutput{
|
|
Cash: 25000.00,
|
|
Total: 52337.50,
|
|
}, nil
|
|
}
|
|
|
|
// -- Analysis tools -----------------------------------------------------------
|
|
|
|
type GetHistoricalDataInput struct {
|
|
Symbol string `json:"symbol" jsonschema:"ticker symbol"`
|
|
Period string `json:"period,omitempty" jsonschema:"time period: 1d, 5d, 1mo, 3mo, 1y (default: 1mo)"`
|
|
Interval string `json:"interval,omitempty" jsonschema:"data interval: 1m, 5m, 1h, 1d (default: 1d)"`
|
|
}
|
|
|
|
type DataPoint struct {
|
|
Date string `json:"date"`
|
|
Open float64 `json:"open"`
|
|
High float64 `json:"high"`
|
|
Low float64 `json:"low"`
|
|
Close float64 `json:"close"`
|
|
Volume int `json:"volume"`
|
|
}
|
|
|
|
type GetHistoricalDataOutput struct {
|
|
Symbol string `json:"symbol"`
|
|
Data []DataPoint `json:"data"`
|
|
}
|
|
|
|
func getHistoricalData(_ context.Context, _ *mcp.CallToolRequest, in GetHistoricalDataInput) (*mcp.CallToolResult, GetHistoricalDataOutput, error) {
|
|
return nil, GetHistoricalDataOutput{
|
|
Symbol: in.Symbol,
|
|
Data: []DataPoint{
|
|
{Date: "2025-01-13", Open: 183.00, High: 185.00, Low: 182.50, Close: 184.75, Volume: 1200000},
|
|
{Date: "2025-01-14", Open: 184.75, High: 186.50, Low: 184.00, Close: 185.50, Volume: 1350000},
|
|
{Date: "2025-01-15", Open: 185.50, High: 187.25, Low: 185.00, Close: 186.00, Volume: 1100000},
|
|
},
|
|
}, nil
|
|
}
|