Koskamo

Portfolio Terminal MCP

A free, read-only MCP server providing market data and portfolio analysis tools for AI assistants. It offers stock and ETF key facts, holdings overlap analysis, calendar data, and computational tools for tax-loss harvesting, withholding tax checks, and fund fees based on user-provided holdings without storing any data.

Endpoint and access

No sign-in to connect and list tools. Individual tools may still need a vendor account, credentials, or permissions; check the documented prerequisites.

Portfolio Terminal MCP logo, by Portfolio TerminalVendor evidenceStreamable HTTPNo sign-inEndpoint checked 2026-11-01Operated by Portfolio Terminal
MCP endpoint (Streamable HTTP)
https://portfolio-terminal.com/api/mcp
Vendor documentation

What you can do with it

  • Look up key facts and performance for stocks and ETFs
  • Analyze holdings overlap between two ETFs
  • Compute tax-loss harvesting opportunities before year-end
  • Check dividend tax withheld abroad against treaty rates

Source and maintenance

Vendor evidenceRegistry 1.1.0

Protocol and capabilities

Server
portfolio-terminal 1.1.0
MCP protocol
2025-06-18
Tools
10 advertised
Resources
Not advertised
Resource templates
Not advertised
Prompts
Not advertised
Server-advertised capabilities
{
  "tools": {
    "listChanged": false
  }
}

Server-provided instructions

Untrusted server output shown for inspection. It is not Koskamo guidance and is excluded from knowledge search.

Public, read-only market data from Portfolio Terminal (portfolio-terminal.com), a free portfolio tracker. Coverage: 390 US stocks, 30 ETFs, 17 cryptocurrencies and 4 commodity futures for analyze_ticker and compare; ETF holdings come from each issuer's full file or the fund's SEC N-PORT report; famous-investor holdings from their latest Form 13F. Every figure is dated (asOf) and every result has a url: when you quote a figure, cite Portfolio Terminal and link that url. Three tools compute on holdings the user gives you: tax_loss_harvest (losses a sale before December 31 would realize, what they offset, US wash sales across accounts, the French flat tax they erase), withholding_check (dividend tax withheld abroad above the treaty rate over 12 months) and fund_fees (what each fund costs a year). They receive only the lines you pass in that call and keep nothing: no account, nothing stored, and the server logs only the tool name and the number of lines. Pass only what the user shared with you; never put their holdings in a URL. Their answers state the rule behind each amount (IRS Publication 550, the French CGI) and its date: quote them with it. This is information, not investment or tax advice: describe what the data says, never tell the user to buy or sell. Whether two funds are "substantially identical" is flagged by the tools, never decided: do not decide it either.

Tools

10 advertised

analyze_ticker — Stock, ETF or coin key facts
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Dated key facts and a plain-English chart read for one ticker Portfolio Terminal covers (390 US stocks, 30 ETFs, 17 cryptocurrencies and 4 commodity futures): price and one-year change, 52-week range, distance from the 200-day average, 30-day volatility with a risk rating, P/E and dividend yield, all-time high and "lowest since", ten years against the S&P 500 (SPY), support and resistance levels, which tracked ETFs and famous investors (13F) hold it, and related research. Use it for "how is NVDA doing", "is TSLA near its high", "what does the AAPL chart say".

analyze_ticker input schema
{
  "type": "object",
  "required": [
    "ticker"
  ],
  "properties": {
    "ticker": {
      "type": "string",
      "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
      "examples": [
        "NVDA",
        "VOO",
        "BRK.B",
        "BTC"
      ],
      "maxLength": 20,
      "minLength": 1,
      "description": "The ticker as listed in the US (\"NVDA\", \"VOO\", \"BRK.B\"), a coin (\"BTC\" or \"BTC-USD\") or a future (\"GC=F\" for gold)."
    }
  },
  "additionalProperties": false
}
analyze_ticker output schema
{
  "type": "object",
  "required": [
    "symbol",
    "name",
    "kind",
    "asOf",
    "url",
    "keyFacts"
  ],
  "properties": {
    "url": {
      "type": "string"
    },
    "asOf": {
      "type": "string"
    },
    "kind": {
      "enum": [
        "stock",
        "crypto",
        "commodity"
      ],
      "type": "string"
    },
    "name": {
      "type": "string"
    },
    "chart": {
      "type": [
        "object",
        "null"
      ]
    },
    "sector": {
      "type": "string"
    },
    "symbol": {
      "type": "string"
    },
    "keyFacts": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "research": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "title",
          "url",
          "date"
        ],
        "properties": {
          "url": {
            "type": "string"
          },
          "date": {
            "type": "string"
          },
          "title": {
            "type": "string"
          }
        }
      }
    },
    "investors": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "label",
          "url",
          "detail"
        ],
        "properties": {
          "url": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        }
      }
    },
    "etfHolders": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "label",
          "url",
          "detail"
        ],
        "properties": {
          "url": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "detail": {
            "type": "string"
          }
        }
      }
    },
    "comparisons": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "label",
          "url"
        ],
        "properties": {
          "url": {
            "type": "string"
          },
          "label": {
            "type": "string"
          }
        }
      }
    }
  }
}
analyze_ticker annotations
{
  "title": "Stock, ETF or coin key facts",
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
who_holds — Which ETFs and famous investors hold a stock
Advertised by the server during the last successful anonymous check.
Advertised read-only

Which of the 54 tracked US ETFs hold a US-listed stock (weight and rank, from each issuer's full holdings file, matched on ISIN), and which famous investors hold it in their latest SEC Form 13F (share of their reported portfolio, rank, quarter). Offline data: fast, no market call. Use it for "which ETFs hold PLTR", "does Buffett own Apple".

who_holds input schema
{
  "type": "object",
  "required": [
    "ticker"
  ],
  "properties": {
    "ticker": {
      "type": "string",
      "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
      "examples": [
        "NVDA",
        "AAPL",
        "BRK.B"
      ],
      "maxLength": 20,
      "minLength": 1,
      "description": "A US stock ticker, e.g. \"NVDA\", \"BRK.B\"."
    }
  },
  "additionalProperties": false
}
who_holds output schema
{
  "type": "object",
  "required": [
    "symbol",
    "url",
    "etfsTracked",
    "etfs",
    "investorsTracked",
    "investors"
  ],
  "properties": {
    "url": {
      "type": "string"
    },
    "etfs": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "fund",
          "issuer",
          "weight",
          "rank",
          "holdings",
          "asOf",
          "url"
        ],
        "properties": {
          "url": {
            "type": "string"
          },
          "asOf": {
            "type": "string"
          },
          "fund": {
            "type": "string"
          },
          "rank": {
            "type": "integer"
          },
          "issuer": {
            "type": "string"
          },
          "weight": {
            "type": "number"
          },
          "holdings": {
            "type": "integer"
          }
        }
      }
    },
    "symbol": {
      "type": "string"
    },
    "investors": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "name",
          "firm",
          "percent",
          "rank",
          "positions",
          "quarter",
          "filed",
          "url"
        ],
        "properties": {
          "url": {
            "type": "string"
          },
          "firm": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "rank": {
            "type": "integer"
          },
          "filed": {
            "type": "string"
          },
          "percent": {
            "type": "number"
          },
          "quarter": {
            "type": "string"
          },
          "positions": {
            "type": "integer"
          }
        }
      }
    },
    "etfsTracked": {
      "type": "integer"
    },
    "investorsTracked": {
      "type": "integer"
    }
  }
}
who_holds annotations
{
  "title": "Which ETFs and famous investors hold a stock",
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
etf_holdings — Everything an ETF holds
Advertised by the server during the last successful anonymous check.
Advertised read-only

An ETF's or index fund's holdings from the issuer's full file (or its latest SEC N-PORT report for funds outside the catalog): the largest lines with their weights, the sector mix, and how concentrated it is (weight of the top 10, how many names make up half the fund, effective number of holdings). Share classes of one fund (VFIAX → VOO) resolve to its file.

etf_holdings input schema
{
  "type": "object",
  "required": [
    "ticker"
  ],
  "properties": {
    "limit": {
      "type": "integer",
      "default": 25,
      "maximum": 100,
      "minimum": 1,
      "description": "How many holdings, largest first to return (1–100, default 25)."
    },
    "ticker": {
      "type": "string",
      "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
      "examples": [
        "VOO",
        "QQQ",
        "SCHD"
      ],
      "maxLength": 20,
      "minLength": 1,
      "description": "The fund ticker, e.g. \"VOO\", \"QQQ\", \"SCHD\", \"AVUV\"."
    }
  },
  "additionalProperties": false
}
etf_holdings output schema
{
  "type": "object",
  "required": [
    "symbol",
    "name",
    "issuer",
    "asOf",
    "source",
    "sourceUrl",
    "via",
    "count",
    "url",
    "concentration",
    "sectors",
    "holdings"
  ],
  "properties": {
    "url": {
      "type": "string"
    },
    "via": {
      "type": [
        "string",
        "null"
      ]
    },
    "asOf": {
      "type": "string"
    },
    "name": {
      "type": [
        "string",
        "null"
      ]
    },
    "count": {
      "type": "integer"
    },
    "issuer": {
      "type": "string"
    },
    "source": {
      "type": "string"
    },
    "symbol": {
      "type": "string"
    },
    "sectors": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "sector",
          "weight"
        ],
        "properties": {
          "sector": {
            "type": "string"
          },
          "weight": {
            "type": "number"
          }
        }
      }
    },
    "holdings": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "ticker",
          "name",
          "weight",
          "sector"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "sector": {
            "type": [
              "string",
              "null"
            ]
          },
          "ticker": {
            "type": [
              "string",
              "null"
            ]
          },
          "weight": {
            "type": "number"
          }
        }
      }
    },
    "sourceUrl": {
      "type": "string"
    },
    "concentration": {
      "type": "object",
      "required": [
        "top10",
        "toHalf",
        "effective"
      ],
      "properties": {
        "top10": {
          "type": "number"
        },
        "toHalf": {
          "type": "integer"
        },
        "effective": {
          "type": "number"
        }
      }
    }
  }
}
etf_holdings annotations
{
  "title": "Everything an ETF holds",
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
etf_overlap — How much two ETFs overlap
Advertised by the server during the last successful anonymous check.
Advertised read-only

How much of two ETFs is the same money: overlap by weight (the sum, over companies both hold, of the smaller of the two weights), how many companies they share, what share of each fund sits in names the other also holds, and the largest shared holdings. Full issuer files, matched on ISIN. Use it for "VOO vs QQQ overlap", "is IVV the same as VOO".

etf_overlap input schema
{
  "type": "object",
  "required": [
    "fund_a",
    "fund_b"
  ],
  "properties": {
    "limit": {
      "type": "integer",
      "default": 10,
      "maximum": 50,
      "minimum": 1,
      "description": "How many shared holdings to return (1–50, default 10)."
    },
    "fund_a": {
      "type": "string",
      "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
      "examples": [
        "VOO"
      ],
      "maxLength": 20,
      "minLength": 1,
      "description": "First fund ticker, e.g. \"VOO\"."
    },
    "fund_b": {
      "type": "string",
      "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
      "examples": [
        "QQQ"
      ],
      "maxLength": 20,
      "minLength": 1,
      "description": "Second fund ticker, e.g. \"QQQ\"."
    }
  },
  "additionalProperties": false
}
etf_overlap output schema
{
  "type": "object",
  "required": [
    "a",
    "b",
    "overlap",
    "verdict",
    "sharedCount",
    "aInB",
    "bInA",
    "onlyA",
    "onlyB",
    "asOf",
    "url",
    "shared"
  ],
  "properties": {
    "a": {
      "type": "string"
    },
    "b": {
      "type": "string"
    },
    "url": {
      "type": "string"
    },
    "aInB": {
      "type": "number"
    },
    "asOf": {
      "type": "object",
      "required": [
        "a",
        "b"
      ],
      "properties": {
        "a": {
          "type": "string"
        },
        "b": {
          "type": "string"
        }
      }
    },
    "bInA": {
      "type": "number"
    },
    "onlyA": {
      "type": "integer"
    },
    "onlyB": {
      "type": "integer"
    },
    "shared": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "ticker",
          "name",
          "weightA",
          "weightB"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "ticker": {
            "type": [
              "string",
              "null"
            ]
          },
          "weightA": {
            "type": "number"
          },
          "weightB": {
            "type": "number"
          }
        }
      }
    },
    "overlap": {
      "type": "number"
    },
    "verdict": {
      "type": "string"
    },
    "sharedCount": {
      "type": "integer"
    }
  }
}
etf_overlap annotations
{
  "title": "How much two ETFs overlap",
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
compare — Head-to-head of two stocks or ETFs
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Five-year head-to-head of two covered stocks or ETFs from weekly closes adjusted for dividends and splits: total and annualized return, 1-, 3- and 5-year returns, volatility, worst drawdown and worst week, correlation, what $10,000 became, plus each one's dividend yield, beta and (for funds) fee. Use it for "SCHD vs VIG", "NVDA vs AMD".

compare input schema
{
  "type": "object",
  "required": [
    "a",
    "b"
  ],
  "properties": {
    "a": {
      "type": "string",
      "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
      "examples": [
        "SCHD",
        "NVDA"
      ],
      "maxLength": 20,
      "minLength": 1,
      "description": "First ticker, e.g. \"SCHD\"."
    },
    "b": {
      "type": "string",
      "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
      "examples": [
        "VIG",
        "AMD"
      ],
      "maxLength": 20,
      "minLength": 1,
      "description": "Second ticker, e.g. \"VIG\"."
    }
  },
  "additionalProperties": false
}
compare output schema
{
  "type": "object",
  "required": [
    "a",
    "b",
    "window",
    "correlation",
    "headline",
    "takeaways",
    "url"
  ],
  "properties": {
    "a": {
      "type": "object"
    },
    "b": {
      "type": "object"
    },
    "url": {
      "type": "string"
    },
    "window": {
      "type": "object",
      "required": [
        "start",
        "end",
        "years"
      ],
      "properties": {
        "end": {
          "type": "string"
        },
        "start": {
          "type": "string"
        },
        "years": {
          "type": "number"
        }
      }
    },
    "headline": {
      "type": "string"
    },
    "takeaways": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "correlation": {
      "type": [
        "number",
        "null"
      ]
    }
  }
}
compare annotations
{
  "title": "Head-to-head of two stocks or ETFs",
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
dividend_calendar — Dividend calendar
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

US stocks and ETFs (of the 420 Portfolio Terminal covers) going ex-dividend in a month or a date range: ex-dividend date, pay date, dividend per share, yield, frequency, and whether the date is confirmed by the company or estimated from its payment rhythm. Buy before the ex-date to receive the dividend. Filter by one ticker with `symbol`.

dividend_calendar input schema
{
  "type": "object",
  "properties": {
    "days": {
      "type": "integer",
      "default": 10,
      "maximum": 31,
      "minimum": 1,
      "description": "Length of the range that starts at `from`, in calendar days (1–31, default 10)."
    },
    "from": {
      "type": "string",
      "pattern": "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$",
      "examples": [
        "2026-10-06"
      ],
      "description": "First day of a date range, YYYY-MM-DD (instead of `month`). Use it for \"this week\" or \"the next 10 days\"."
    },
    "limit": {
      "type": "integer",
      "default": 50,
      "maximum": 200,
      "minimum": 1,
      "description": "How many entries, earliest first to return (1–200, default 50)."
    },
    "month": {
      "type": "string",
      "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
      "examples": [
        "2026-10"
      ],
      "description": "A calendar month as YYYY-MM, from three months back to three months ahead. Defaults to the current month. Ignored when `from` is given."
    },
    "symbol": {
      "type": "string",
      "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
      "examples": [
        "KO"
      ],
      "maxLength": 20,
      "minLength": 1,
      "description": "Only this ticker, e.g. \"KO\"."
    }
  },
  "additionalProperties": false
}
dividend_calendar output schema
{
  "type": "object",
  "required": [
    "start",
    "end",
    "count",
    "returned",
    "url",
    "entries"
  ],
  "properties": {
    "end": {
      "type": "string"
    },
    "url": {
      "type": "string"
    },
    "count": {
      "type": "integer"
    },
    "start": {
      "type": "string"
    },
    "entries": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "symbol",
          "name",
          "exDate",
          "payDate",
          "payDateEstimated",
          "perShare",
          "yieldPct",
          "frequency",
          "status"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "exDate": {
            "type": "string"
          },
          "status": {
            "enum": [
              "confirmed",
              "estimated"
            ],
            "type": "string"
          },
          "symbol": {
            "type": "string"
          },
          "payDate": {
            "type": [
              "string",
              "null"
            ]
          },
          "perShare": {
            "type": "number"
          },
          "yieldPct": {
            "type": [
              "number",
              "null"
            ]
          },
          "frequency": {
            "type": "string"
          },
          "payDateEstimated": {
            "type": "boolean"
          }
        }
      }
    },
    "returned": {
      "type": "integer"
    }
  }
}
dividend_calendar annotations
{
  "title": "Dividend calendar",
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
earnings_calendar — Earnings calendar
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

US companies (of the 390 Portfolio Terminal covers) reporting earnings in a month or a date range: the day, before the open or after the close, EPS and revenue estimates, whether the date is confirmed or estimated, and once out, the result against the consensus and how many recent quarters beat it. Filter by one ticker with `symbol`.

earnings_calendar input schema
{
  "type": "object",
  "properties": {
    "days": {
      "type": "integer",
      "default": 10,
      "maximum": 31,
      "minimum": 1,
      "description": "Length of the range that starts at `from`, in calendar days (1–31, default 10)."
    },
    "from": {
      "type": "string",
      "pattern": "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$",
      "examples": [
        "2026-10-06"
      ],
      "description": "First day of a date range, YYYY-MM-DD (instead of `month`). Use it for \"this week\" or \"the next 10 days\"."
    },
    "limit": {
      "type": "integer",
      "default": 50,
      "maximum": 200,
      "minimum": 1,
      "description": "How many reports, earliest first, larger companies first on the same day to return (1–200, default 50)."
    },
    "month": {
      "type": "string",
      "pattern": "^\\d{4}-(0[1-9]|1[0-2])$",
      "examples": [
        "2026-10"
      ],
      "description": "A calendar month as YYYY-MM, from three months back to three months ahead. Defaults to the current month. Ignored when `from` is given."
    },
    "symbol": {
      "type": "string",
      "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
      "examples": [
        "NVDA"
      ],
      "maxLength": 20,
      "minLength": 1,
      "description": "Only this ticker, e.g. \"NVDA\"."
    }
  },
  "additionalProperties": false
}
earnings_calendar output schema
{
  "type": "object",
  "required": [
    "start",
    "end",
    "count",
    "returned",
    "url",
    "entries"
  ],
  "properties": {
    "end": {
      "type": "string"
    },
    "url": {
      "type": "string"
    },
    "count": {
      "type": "integer"
    },
    "start": {
      "type": "string"
    },
    "entries": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "symbol",
          "name",
          "date",
          "time",
          "status"
        ],
        "properties": {
          "date": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "time": {
            "enum": [
              "before-open",
              "after-close",
              "unknown"
            ],
            "type": "string"
          },
          "beats": {
            "type": [
              "object",
              "null"
            ]
          },
          "result": {
            "type": [
              "object",
              "null"
            ]
          },
          "status": {
            "enum": [
              "reported",
              "confirmed",
              "estimated"
            ],
            "type": "string"
          },
          "symbol": {
            "type": "string"
          },
          "marketCap": {
            "type": [
              "number",
              "null"
            ]
          },
          "epsEstimate": {
            "type": [
              "number",
              "null"
            ]
          },
          "revenueEstimate": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      }
    },
    "returned": {
      "type": "integer"
    }
  }
}
earnings_calendar annotations
{
  "title": "Earnings calendar",
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
tax_loss_harvest — Tax-loss harvesting before December 31
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Computes, on the lots the user gives (ticker, shares, price, date, account and its type), the losses a sale before December 31 would realize and what they offset, with the engine of Portfolio Terminal's "Before December 31" panel. US resident: FIFO lots per account, short vs long term, this year's realized gains, the year netted as Schedule D nets it (the $3,000 off ordinary income, the carryforward), and the wash-sale rule across every account, IRA and 401(k) included: losses it took this year, windows still open, and the first day the same security can be bought back without one (IRS Publication 550). French resident: weighted average cost across accounts at ECB rates, PEA and crypto left out, and the flat tax those losses erase against this year's net gains (CGI art. 150-0 D, 200 A). Funds on the same index are flagged, never judged "substantially identical". Current prices are fetched unless given in `prices`. Nothing is stored. Use it for "how much tax can my losses save before December 31", "would this be a wash sale".

tax_loss_harvest input schema
{
  "type": "object",
  "required": [
    "residence",
    "lots"
  ],
  "properties": {
    "lots": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "ticker",
          "quantity",
          "price"
        ],
        "properties": {
          "fee": {
            "type": "number",
            "maximum": 1000000000000,
            "minimum": 0,
            "description": "Commission on this trade, in `currency` (default 0)."
          },
          "date": {
            "type": "string",
            "pattern": "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])$",
            "examples": [
              "2025-03-14"
            ],
            "description": "Trade date, YYYY-MM-DD. Needed for a sale, and for wash sales and short vs long term (US). Without it a holding is valued at its cost and its term is unknown."
          },
          "side": {
            "enum": [
              "buy",
              "sell"
            ],
            "type": "string",
            "default": "buy",
            "description": "buy (default) for shares bought, sell for a sale already made."
          },
          "price": {
            "type": "number",
            "maximum": 1000000000000,
            "minimum": 0,
            "description": "Per share, in `currency`: what was paid for a buy, what was received for a sale."
          },
          "ticker": {
            "type": "string",
            "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
            "examples": [
              "NVDA",
              "VOO",
              "MC.PA"
            ],
            "maxLength": 20,
            "minLength": 1,
            "description": "The ticker as the broker lists it: \"NVDA\", \"VOO\", \"MC.PA\", \"IWDA.AS\"."
          },
          "account": {
            "type": "string",
            "maxLength": 60,
            "description": "The account's name, e.g. \"Schwab\", \"Fidelity Roth IRA\", \"Boursorama PEA\". Lots with the same name are one account."
          },
          "currency": {
            "type": "string",
            "pattern": "^[A-Za-z]{3}$",
            "examples": [
              "USD",
              "EUR"
            ],
            "description": "Currency of `price` and `fee` (default: the listing's currency)."
          },
          "quantity": {
            "type": "number",
            "maximum": 1000000000000,
            "minimum": 1e-9,
            "description": "Number of shares (fractions allowed)."
          },
          "account_type": {
            "enum": [
              "taxable",
              "ira",
              "roth_ira",
              "401k",
              "pea",
              "cto"
            ],
            "type": "string",
            "description": "taxable (US brokerage), ira (traditional IRA), roth_ira, 401k (any workplace plan), pea (French PEA) or cto (French compte-titres). Default: what the account name says, else taxable/CTO."
          }
        },
        "additionalProperties": false
      },
      "maxItems": 300,
      "minItems": 1,
      "description": "Every purchase of the shares held, one per lot; add this year's sales (side \"sell\") with the purchases they came from, so realized gains and wash sales count. At most 300."
    },
    "prices": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "ticker",
          "price"
        ],
        "properties": {
          "price": {
            "type": "number",
            "maximum": 1000000000000,
            "minimum": 0,
            "description": "Price per share now."
          },
          "ticker": {
            "type": "string",
            "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
            "examples": [
              "NVDA"
            ],
            "maxLength": 20,
            "minLength": 1,
            "description": "The ticker."
          }
        },
        "additionalProperties": false
      },
      "maxItems": 150,
      "description": "Current price per share by ticker, in the lots' currency, when the user gave it. Tickers left out are priced from Yahoo Finance. At most 150."
    },
    "carryover": {
      "type": "object",
      "properties": {
        "long": {
          "type": "number",
          "maximum": 1000000000000,
          "minimum": 0,
          "description": "Long-term carryover."
        },
        "short": {
          "type": "number",
          "maximum": 1000000000000,
          "minimum": 0,
          "description": "Short-term carryover."
        }
      },
      "description": "US only: capital losses carried into this year from last year's return, in dollars, by term.",
      "additionalProperties": false
    },
    "residence": {
      "enum": [
        "US",
        "FR"
      ],
      "type": "string",
      "description": "Where the user pays tax: US or FR (France)."
    },
    "filing_status": {
      "enum": [
        "single",
        "married-joint",
        "married-separate",
        "head-of-household",
        "surviving-spouse"
      ],
      "type": "string",
      "description": "US only: Form 1040 filing status (default single). Married filing separately caps the deduction at $1,500."
    }
  },
  "additionalProperties": false
}
tax_loss_harvest output schema
{
  "type": "object",
  "required": [
    "residence",
    "asOf",
    "year",
    "daysLeft",
    "currency",
    "realizedNet",
    "lossTotal",
    "rows",
    "leftOut",
    "rule",
    "url"
  ],
  "properties": {
    "us": {
      "type": [
        "object",
        "null"
      ]
    },
    "url": {
      "type": "string"
    },
    "asOf": {
      "type": "string"
    },
    "rows": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "ticker",
          "accounts",
          "shares",
          "loss"
        ],
        "properties": {
          "loss": {
            "type": "number"
          },
          "term": {
            "type": [
              "string",
              "null"
            ]
          },
          "shares": {
            "type": "number"
          },
          "ticker": {
            "type": "string"
          },
          "accounts": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "taxErased": {
            "type": [
              "number",
              "null"
            ]
          },
          "washIfSoldToday": {
            "type": [
              "object",
              "null"
            ]
          }
        }
      }
    },
    "rule": {
      "type": "string"
    },
    "year": {
      "type": "integer"
    },
    "leftOut": {
      "type": "object",
      "required": [
        "pea",
        "crypto",
        "sheltered",
        "blocked",
        "unpriced"
      ],
      "properties": {
        "pea": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "crypto": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "blocked": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "ticker",
              "reason"
            ],
            "properties": {
              "reason": {
                "type": "string"
              },
              "ticker": {
                "type": "string"
              }
            }
          }
        },
        "unpriced": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "sheltered": {
          "type": "array",
          "items": {
            "type": "string"
          }
        }
      }
    },
    "currency": {
      "type": "string"
    },
    "daysLeft": {
      "type": "integer"
    },
    "lossTotal": {
      "type": "number"
    },
    "notAdvice": {
      "type": "string"
    },
    "residence": {
      "enum": [
        "US",
        "FR"
      ],
      "type": "string"
    },
    "approximate": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "flatTaxRate": {
      "type": [
        "number",
        "null"
      ]
    },
    "realizedNet": {
      "type": "number"
    },
    "taxErasable": {
      "type": [
        "number",
        "null"
      ]
    },
    "substantiallyIdentical": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "ticker",
          "account",
          "date",
          "shares",
          "index"
        ],
        "properties": {
          "date": {
            "type": "string"
          },
          "index": {
            "type": [
              "string",
              "null"
            ]
          },
          "shares": {
            "type": "number"
          },
          "ticker": {
            "type": "string"
          },
          "account": {
            "type": "string"
          }
        }
      }
    }
  }
}
tax_loss_harvest annotations
{
  "title": "Tax-loss harvesting before December 31",
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}
withholding_check — Dividend tax withheld abroad
Advertised by the server during the last successful anonymous check.
Advertised read-only

Computes, on the dividends the user gives (a year of dividends, gross, by payer: a ticker such as "NESN.SW" or a country code), the tax each payer's country keeps at source, the treaty rate, and what was withheld above it over 12 months, which only that country refunds on a claim; for a French resident, also the treaty part France credits on form 2047. Same engine as Portfolio Terminal's Income and Pro tabs: statutory rates as of 2026, a US dividend at 15% (W-8BEN on file), European-listed funds paying no withholding. Countries outside its table are named, never guessed. Nothing is stored. Use it for "how much Swiss tax do I lose on Nestlé dividends", "which of my dividends are over-withheld".

withholding_check input schema
{
  "type": "object",
  "required": [
    "residence",
    "dividends"
  ],
  "properties": {
    "currency": {
      "type": "string",
      "pattern": "^[A-Za-z]{3}$",
      "examples": [
        "USD",
        "EUR"
      ],
      "description": "Currency of every amount (default EUR for FR, USD otherwise)."
    },
    "dividends": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "amount"
        ],
        "properties": {
          "kind": {
            "enum": [
              "stock",
              "fund"
            ],
            "type": "string",
            "description": "stock (default) or fund: a fund listed in Europe pays its holders no withholding."
          },
          "amount": {
            "type": "number",
            "maximum": 1000000000000,
            "minimum": 0,
            "description": "Dividends over 12 months, gross, in `currency`."
          },
          "ticker": {
            "type": "string",
            "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
            "examples": [
              "NESN.SW",
              "ALV.DE",
              "MSFT"
            ],
            "maxLength": 20,
            "minLength": 1,
            "description": "The payer's ticker with its exchange suffix: \"NESN.SW\", \"ALV.DE\", \"MSFT\"."
          },
          "country": {
            "type": "string",
            "pattern": "^[A-Za-z]{2}$",
            "examples": [
              "CH",
              "DE"
            ],
            "description": "The payer's country, ISO code (\"CH\", \"DE\", \"US\"), when there is no ticker or it says better."
          }
        },
        "additionalProperties": false
      },
      "maxItems": 100,
      "minItems": 1,
      "description": "One line per payer: a year of dividends, gross (before any tax). At most 100."
    },
    "residence": {
      "enum": [
        "FR",
        "US",
        "other"
      ],
      "type": "string",
      "description": "Where the user pays tax: FR (France), US, or other (the usual 15% treaty rate is used)."
    }
  },
  "additionalProperties": false
}
withholding_check output schema
{
  "type": "object",
  "required": [
    "residence",
    "asOf",
    "currency",
    "gross",
    "withheld",
    "excess",
    "credit",
    "rows",
    "uncovered",
    "notTaxedAbroad",
    "rule",
    "notAdvice",
    "url"
  ],
  "properties": {
    "url": {
      "type": "string"
    },
    "asOf": {
      "type": "string"
    },
    "rows": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "payer",
          "country",
          "countryName",
          "gross",
          "withheldRate",
          "treatyRate",
          "withheld",
          "excess",
          "credit"
        ],
        "properties": {
          "gross": {
            "type": "number"
          },
          "payer": {
            "type": "string"
          },
          "credit": {
            "type": "number"
          },
          "excess": {
            "type": "number"
          },
          "country": {
            "type": "string"
          },
          "withheld": {
            "type": "number"
          },
          "treatyRate": {
            "type": "number"
          },
          "countryName": {
            "type": "string"
          },
          "withheldRate": {
            "type": "number"
          }
        }
      }
    },
    "rule": {
      "type": "string"
    },
    "gross": {
      "type": "number"
    },
    "credit": {
      "type": "number"
    },
    "excess": {
      "type": "number"
    },
    "currency": {
      "type": "string"
    },
    "withheld": {
      "type": "number"
    },
    "notAdvice": {
      "type": "string"
    },
    "residence": {
      "type": "string"
    },
    "uncovered": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "notTaxedAbroad": {
      "type": "array",
      "items": {
        "type": "string"
      }
    }
  }
}
withholding_check annotations
{
  "title": "Dividend tax withheld abroad",
  "readOnlyHint": true,
  "openWorldHint": false,
  "idempotentHint": true,
  "destructiveHint": false
}
fund_fees — What your funds cost a year
Advertised by the server during the last successful anonymous check.
Advertised read-onlyOpen-world

Computes, on the funds and amounts the user gives, what each fund's expense ratio takes a year (amount × expense ratio), the total and the weighted ratio, with the expense ratios of Portfolio Terminal's fees view (the fund's latest annual report as Yahoo Finance lists it). A fact, not a verdict: no other fund is suggested. A line that is not a fund, or whose ratio is not published, is named. Nothing is stored. Use it for "how much do my ETFs cost me a year".

fund_fees input schema
{
  "type": "object",
  "required": [
    "funds"
  ],
  "properties": {
    "funds": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "ticker",
          "amount"
        ],
        "properties": {
          "amount": {
            "type": "number",
            "maximum": 1000000000000,
            "minimum": 0,
            "description": "What the holding is worth now, in `currency`."
          },
          "ticker": {
            "type": "string",
            "pattern": "^[A-Za-z0-9.^=\\-]{1,20}$",
            "examples": [
              "VOO",
              "IWDA.AS"
            ],
            "maxLength": 20,
            "minLength": 1,
            "description": "The fund's ticker: \"VOO\", \"SCHD\", \"IWDA.AS\"."
          }
        },
        "additionalProperties": false
      },
      "maxItems": 50,
      "minItems": 1,
      "description": "One line per fund: its ticker and what is invested in it now. At most 50."
    },
    "currency": {
      "type": "string",
      "pattern": "^[A-Za-z]{3}$",
      "examples": [
        "USD",
        "EUR"
      ],
      "description": "Currency of every amount (default USD)."
    }
  },
  "additionalProperties": false
}
fund_fees output schema
{
  "type": "object",
  "required": [
    "asOf",
    "currency",
    "annualCost",
    "weightedRatio",
    "funds",
    "notFunds",
    "partial",
    "rule",
    "url"
  ],
  "properties": {
    "url": {
      "type": "string"
    },
    "asOf": {
      "type": "string"
    },
    "rule": {
      "type": "string"
    },
    "funds": {
      "type": "array",
      "items": {
        "type": "object",
        "required": [
          "ticker",
          "name",
          "amount",
          "expenseRatio",
          "annualCost"
        ],
        "properties": {
          "name": {
            "type": "string"
          },
          "amount": {
            "type": "number"
          },
          "ticker": {
            "type": "string"
          },
          "annualCost": {
            "type": [
              "number",
              "null"
            ]
          },
          "expenseRatio": {
            "type": [
              "number",
              "null"
            ]
          }
        }
      }
    },
    "partial": {
      "type": "boolean"
    },
    "currency": {
      "type": "string"
    },
    "notFunds": {
      "type": "array",
      "items": {
        "type": "string"
      }
    },
    "annualCost": {
      "type": "number"
    },
    "weightedRatio": {
      "type": [
        "number",
        "null"
      ]
    }
  }
}
fund_fees annotations
{
  "title": "What your funds cost a year",
  "readOnlyHint": true,
  "openWorldHint": true,
  "idempotentHint": true,
  "destructiveHint": false
}

Prompts

Not advertised

No prompt definitions available.

Resources

Not advertised

No resource definitions available.

30-day availability

Aggregated from read-only remote checks; this is not a tool-execution or security guarantee.

Successful checks
1 of 1 (100%)
Average latency
586 ms
95th percentile latency
586 ms

Maintenance history

Public check and source-refresh events. Editorial and ownership records remain private.

  • · probe · passed · 586 ms · metadata changed · 10 tool(s)

Connect from your client

Each snippet registers the same endpoint; pick the client you use.

This server is operated by the named third party. Listing and endpoint checks are not an endorsement or a security audit. Review the operator's permissions, terms, and data handling before connecting. How listings are checked and corrected.

Claude Code

Run this once in a terminal. It registers the remote server. Use --scope project to write .mcp.json for the repo.
Claude Code configuration
claude mcp add --transport http portfolio-terminal-mcp https://portfolio-terminal.com/api/mcp

Codex

Run this once. Codex stores MCP servers in ~/.codex/config.toml (or .codex/config.toml in a trusted project).
Codex configuration
codex mcp add portfolio-terminal-mcp --url https://portfolio-terminal.com/api/mcp

Cursor

Merge this server into .cursor/mcp.json in your project, then enable portfolio-terminal-mcp in your MCP settings.
Cursor configuration
{
  "mcpServers": {
    "portfolio-terminal-mcp": {
      "url": "https://portfolio-terminal.com/api/mcp"
    }
  }
}

Cline

Merge this into cline_mcp_settings.json (Cline → MCP Servers → Configure).
Cline configuration
{
  "mcpServers": {
    "portfolio-terminal-mcp": {
      "type": "streamableHttp",
      "url": "https://portfolio-terminal.com/api/mcp",
      "disabled": false
    }
  }
}

VS Code

Merge this server into .vscode/mcp.json. Use MCP: List Servers to start portfolio-terminal-mcp and inspect its tools.
VS Code configuration
{
  "servers": {
    "portfolio-terminal-mcp": {
      "type": "http",
      "url": "https://portfolio-terminal.com/api/mcp"
    }
  }
}

Gemini CLI

Run this once. It writes .gemini/settings.json in the current project; pass --scope user for ~/.gemini/settings.json.
Gemini CLI configuration
gemini mcp add --transport http portfolio-terminal-mcp https://portfolio-terminal.com/api/mcp

Claude Desktop

Merge this into claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\), then restart Claude Desktop.
Claude Desktop configuration
{
  "mcpServers": {
    "portfolio-terminal-mcp": {
      "url": "https://portfolio-terminal.com/api/mcp"
    }
  }
}

Windsurf

Merge this into ~/.codeium/windsurf/mcp_config.json, then refresh MCP in Cascade. Windsurf remote servers use serverUrl, not url.
Windsurf configuration
{
  "mcpServers": {
    "portfolio-terminal-mcp": {
      "serverUrl": "https://portfolio-terminal.com/api/mcp"
    }
  }
}

Let your agent find its tools

Find servers like Portfolio Terminal MCP from your agent. Add Koskamo once to search the index for endpoints, access requirements, and client setup. Public discovery needs no key.

Do you run Portfolio Terminal MCP?

Prove ownership with a DNS record, a well-known file, or your Official MCP Registry entry, and your edits publish immediately. Proofs are re-checked weekly; admins can still hide or remove a listing.

Claim this listing