pse-edge-mcp
This server provides read-only access to end-of-day Philippine Stock Exchange (PSE) data, including company profiles, stock quotes, historical prices, financial reports, disclosures, and market indices. It minimizes impact on the upstream PSE Edge portal by caching and serving frozen data when the market is open.
Company Lookup – Search for companies by name or ticker (search_companies), validate a ticker symbol (validate_symbol), and retrieve a company's profile with sector, incorporation date, auditor, and contact info (get_company_profile).
Market & Price Data – Get the latest EOD stock quote with price, change, 52-week range, market cap (get_stock_quote); daily OHLC price history over a date range (get_price_history); PSEi and all 7 sector index levels with daily changes (get_indices); and a market-wide snapshot including index levels and disclosure feeds (get_market_summary).
Disclosures & Filings – Search company announcements by symbol, date range, or type (search_disclosures); full-text search inside disclosure attachments, limited to snippet results (search_disclosure_fulltext); and retrieve full details of a single disclosure including attachment and body HTML links by its edge number (get_disclosure).
Financial Data – Get annual and quarterly balance sheet and income statement highlights (get_financial_highlights) – note that reported units may vary, so always verify unit labels. Also retrieve declared dividends and stock rights with ex-dividend, record, and payment dates, linked to source disclosures (get_dividends_and_rights).
Communication (auth-enabled deployments only) – Send an email to the authenticated user’s own address (send_email).
Key Constraints – All data is end-of-day frozen: no upstream requests while the PSE is open (09:30–15:00 Manila time); queries during market hours return cached data or a MARKET_OPEN_NO_CACHE error. Every result includes freshness metadata (meta.as_of, meta.valid_until, meta.stale). Disclosure attachments are not downloaded or parsed; the server returns URLs for the client to fetch directly.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@pse-edge-mcpshow me the price history of BDO"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
pse-edge-mcp
An MCP server exposing Philippine Stock Exchange data from the PSE Edge portal — quotes, price history, disclosures, financial reports, and market data — to Claude and any other MCP client.
Unofficial. PSE Edge has no public API; this project speaks to the same endpoints the portal's own pages use. It is not affiliated with or endorsed by the PSE. Data is provided as-is for personal/research use, with no warranty.
Contents
Related MCP server: VNStock MCP Server
Features
13 read tools + 1 action tool covering quotes, price history, disclosures (metadata, full-text, and detail), company profiles, financials, dividends, indices, and market summary — plus an attachment resource, two prompts with symbol completion, and tool annotations.
Deliberately gentle on PSE Edge: every unique query hits it at most once per day, and prices follow a strict market-boundary freeze.
OAuth 2.1 + passkeys for humans (no passwords anywhere),
client_credentialsfor headless agents — both opt-in; stdio needs nothing.Postgres optional: zero-config in-memory for local stdio, or a shared cache + an ever-deepening EOD archive when
DATABASE_URLis set.Loud on drift: a nightly canary validates live pages against the real models and alerts only on failure; a restyled page raises an error, never partial data.
Multi-arch container image (amd64 + arm64), gated on necessity — the image contains exactly the runtime dependency closure and nothing else.
Quick start
Claude Desktop / Claude Code (stdio):
uvx pse-edge-mcp{
"mcpServers": {
"pse-edge": { "command": "uvx", "args": ["pse-edge-mcp"] }
}
}A hosted deployment (auth on) is a normal OAuth 2.1 protected resource — modern clients need only the URL and drive the whole flow themselves (details below):
{
"mcpServers": {
"pse-edge": { "url": "https://your-host.example.com/mcp" }
}
}Docker (HTTP + Postgres):
cp .env.example .env # set POSTGRES_PASSWORD
docker compose up --buildDesign: end-of-day prices, fetch-once everything else
Four layers, one direction of dependency. Every read goes through FreezeService.get() with an explicit per-domain policy — no tool ever touches the HTTP client directly. ★ marks the invariant the whole design exists to protect, and it guards prices only.
flowchart TD
C["MCP client"] -- "tool call" --> S["<b>server.py</b> · MCP boundary<br/>validate args · delegate · shape reply"]
S --> R["<b>repositories.py</b> · one per domain<br/>cache key · freeze read · parse · endpoint routing"]
R --> F["<b>service.py</b> · FreezeService ★<br/>3 policies · miss → fetch once"]
F --> P["<b>client.py</b> · PseEdgeClient<br/>throttled HTTPS · single-flight · 2 dialects"]
P --> E["PSE Edge<br/>edge.pse.com.ph"]★ Market-boundary freeze — prices only. A cached stock price is never refetched while the market is open (09:30–15:00 Asia/Manila, trading days) — the last close answers, flagged stale. A price nobody has ever asked for is the one exception: fetched once mid-session and served as identity + previous_close only (every session-moving field withheld), with stale: true plus a meta.note saying it is not a realtime value; the settled figures replace it after the close.
Policy | Applies to | Behaviour |
|
| A cached price is never refetched during a session; a never-cached key is fetched once and surfaces only |
| Companies, disclosures, profiles, financials, dividends, indices, summary | First ask fetches at any hour — once, deduplicated across concurrent callers; every repeat of the same query answers from storage until the next 15:00 close. |
| Disclosure detail by | The object never changes upstream. Fetched once ever; |
If PSE Edge is unreachable and an expired entry exists, tools serve it flagged meta.stale: true rather than discarding real data for an error. EDGE_UNAVAILABLE means unreachable and nothing cached.
Every data tool returns the same envelope — meta is the freshness contract:
{
"data": { /* …StockQuote… */ },
"meta": {
"as_of": "2026-08-06T15:00:00+08:00", // ISO-8601, Asia/Manila
"valid_until": "2026-08-07T15:00:00+08:00", // null when immutable
"from_cache": false,
"stale": false, // true = not a settled EOD value
"data_policy": "EOD-frozen", // "daily-refresh" / "immutable" elsewhere
"note": null // freshness caveat, e.g. "not a realtime value"
}
}Tools, resources, prompts
Tool | Description |
| Find PSE-listed companies by name or ticker |
| Cheap yes/no check that a ticker exists, with its company name and id |
| Latest EOD quote: price, change, 52-wk range, market cap, full field set |
| Daily OHLC series from Edge's chart endpoint |
| Disclosure metadata, market-wide or per company; 50/page with exact totals |
| Search the text inside disclosure attachments, with snippets |
| One disclosure's details plus attachment and body-HTML links; attachments capped at |
| Sector, incorporation, auditor, transfer agent, contacts |
| Annual + quarterly balance sheet and income statement |
| Declared dividends and stock rights, linked to their disclosures |
| PSEi and the 7 sector indices, with signed daily change |
| Index levels plus PSE Edge's homepage disclosure feeds |
| The deployed version of this MCP server itself (matches |
| Email yourself a note (auth-enabled deployments only) |
Beyond tools, the server exposes the attachment resource above, two prompts (market_recap, company_briefing(symbol) — the symbol argument autocompletes from PSE Edge's own lookup), and MCP tool annotations so hosts can auto-approve the read-only tools. It is described for the MCP Registry in server.json.
send_email is the only tool that acts rather than reads. It has no recipient argument: the message always goes to the account that authenticated the session, so it cannot be used as a relay and there is nothing for prompt injection to redirect — which matters because this server returns disclosure text the operator does not control. It appears only on deployments with auth enabled (there is no verified address otherwise), the body is escaped rather than rendered as HTML, and it is capped at 20 messages per user per day.
Disclosure tools return metadata and links only — this server never downloads or parses attachments (beyond the explicit resource read), so your MCP client can fetch the returned URLs itself if it needs the files. Note that Edge's own full-text index is partial (roughly 2023–2025 at last check), so search_disclosure_fulltext is not a substitute for search_disclosures; it reports this limit in its results.
Financial figures are returned exactly as PSE Edge prints them and are never rescaled — Edge's own units labels are inconsistent between its annual and quarterly sections, so each period reports its currency_units for you to check. Index changes are signed here even though Edge prints them unsigned (it shows direction only as a colour and an arrow).
Architecture
The layers
Layer | Owns | Never |
| Argument validation, delegation, reply shaping. Error mapping happens once in | Domain logic, cache keys, parsing, endpoint choices |
| One repository per data domain: the cache key, the freeze read, the parse, the Pydantic model. Endpoint routing lives here. | Depending on the concrete client — only on the protocols below |
|
| — |
| Pure HTTP, MCP-agnostic: token-bucket throttle, single-flight, retries; two request dialects (JSON-body POST for chart | — |
Core class map
Five repositories cover the whole tool surface. Each consumes a narrow source protocol — the concrete client satisfies all five, but no repository knows that, so each is testable with a few-line fake and no HTTP mocking.
Repository | Methods → models | Consumes | Policy / note |
|
|
|
|
|
|
|
|
|
|
| searches |
|
|
|
|
|
|
|
|
|
| — | recipient comes from the bearer token, never an argument |
Protocols and swappable implementations
One switch picks the column: DATABASE_URL unset → in-memory / Null; set → Postgres. Postgres modules import lazily, so a lean install never pays for them.
Protocol |
|
|
|
|
|
|
|
|
|
|
|
| — |
|
|
|
|
HTTP composition — built once, in asgi.py
flowchart LR
H["HealthApp<br/>/health · /health/ready"] --> A["AuthApp<br/>/oauth/* · signup · /account · /privacy"]
A --> M["AuthMiddleware<br/>bearer validation · quotas · usage"]
M --> MCP["MCP app<br/>the tool surface"]/health is liveness and never touches the database; /health/ready is readiness. Behind AuthApp: OAuthService (DCR · PKCE-only · refresh families), PasskeyService (WebAuthn + web sessions), TokenService (opaque pse_ tokens, SHA-256 at rest).
Error family — one root, mapped once in reply()
Error | Meaning |
|
|
|
|
| Edge redesigned a page — loud, never partial |
| Upstream unreachable and nothing cached |
| Retained for client compatibility; no longer raised |
| Action tool needs auth enabled |
| 20 emails / user / day |
Watchdog
A nightly canary (pse-edge-canary, plus a compose service) fetches live pages bypassing the cache and validates the same Pydantic models the repositories build — a 200 with a restyled table is exactly the failure it exists to catch. It still refuses to run while the market is open (the ★ invariant outranks it), emails PSE_OPERATOR_EMAIL only on failure, and exits non-zero so cron notices.
Golden path: one request traced
get_stock_quote("SM") after market close, cold cache:
server.py—validation.pychecks the symbol shape (bad input →INVALID_ARGUMENT), thenreply()wraps the repository call — the only place errors become MCP error payloads.QuoteRepository.quote("SM")— resolvesSM→company_idthroughCompanyRepository, picks the endpoint, builds the cache key. Tools never see any of this.FreezeService.get(key, fetch, policy="EOD-frozen")★ — fresh cache entry → serve it. Market open + cached → serve the last close flaggedstale, never refetch. Market open + never cached → fetch once, labelstale: true+notefor the whole session. Market closed + miss → fetch. Fetch fails but an expired entry exists → serve it flaggedstale.PseEdgeClient.fetch_stock_data_page(company_id)— token bucket (1 req/s), single-flight dedupe, retries. Wire dates areMM-dd-yyyy; the JSON-vs-form dialect is chosen per endpoint.parsers.py→StockQuote— HTML → dict → validated Pydantic model. Any drift in Edge's markup raisesEndpointChangedError.cache.py/archive.py— the entry freezes until the next 15:00 close; daily bars archive opportunistically (a dead database never fails a read).
Connecting to a hosted server
A deployment with auth on is a normal OAuth 2.1 protected resource, so a modern MCP client needs only the URL — it discovers everything else and drives the whole flow itself.
What happens on first connect
Nothing here is manual except the two browser steps in bold.
The client
POSTs to/mcpwith no token and gets 401 carryingWWW-Authenticate: Bearer resource_metadata="…/.well-known/oauth-protected-resource". That header is the entire bootstrap: it tells the client where to look next.It fetches that document, learns which authorization server guards this resource, then reads
/.well-known/oauth-authorization-serverfor the endpoints.It registers itself at
/oauth/register(RFC 7591) — no client secret, no operator involvement, no pre-shared credentials. It gets back aclient_id.It opens
/oauth/authorizein a browser with a PKCE challenge (S256 required).The user signs up or signs in. New users land on
/signup, agree to the (deliberately tiny) data policy, give an email, and receive a link; the link shows a confirm page whose button enrolls a passkey at/enroll— the confirm step exists so a mail scanner's prefetch cannot spend the link. Returning users hit/loginand use the passkey they already have. No password exists anywhere in the system.The user approves the client on a consent screen naming it.
The browser returns to the client with a single-use code; the client exchanges it at
/oauth/tokenwith its PKCE verifier and receives an access token (15 min) and a single-use refresh token (24 h).The client calls
/mcpwithAuthorization: Bearer …and refreshes silently from then on. The user is not asked again.
client ──POST /mcp──────────────▶ 401 + WWW-Authenticate
──GET /.well-known/… ───▶ metadata
──POST /oauth/register ──▶ client_id
──GET /oauth/authorize ─▶ browser: signup/login → passkey → consent
◀───────────────────────── ?code=…
──POST /oauth/token ─────▶ access (15 min) + refresh (24 h)
──POST /mcp + Bearer ────▶ toolsRefresh tokens rotate on every use, and replaying a rotated one revokes that whole session family (RFC 9700 §4.14) — a stolen refresh token gets one use before the theft is detected and the session dies.
Headless agents (client_credentials)
For a LangGraph app, the Anthropic Messages API MCP connector, or any agent that cannot open a browser. No redirect, no passkey, no consent screen — a client id and secret.
1. Provision. Two routes, same result:
From the web (needs no shell — the practical choice on a NAS): set
PSE_ADMIN_EMAILSto your account's email, sign in, and a Machine clients panel appears on/accountwith create and revoke controls. Access is gated to that allowlist — a normal signup never sees it.From the CLI:
pse-edge-admin create-machine-client --name langgraph-app.
Either way client_id and client_secret are shown once. Only the secret's SHA-256 is stored, so it cannot be recovered — only revoked and reissued (from the same account page, or pse-edge-admin revoke-machine-client <client_id>).
2. Mint a token:
curl -s -X POST https://pse.sakayandgo.com/oauth/token \
-d grant_type=client_credentials \
-d client_id=$CLIENT_ID -d client_secret=$CLIENT_SECRET \
-d scope=mcp -d resource=https://pse.sakayandgo.com/mcp{"access_token": "pse_…", "token_type": "Bearer", "expires_in": 3600, "scope": "mcp"}HTTP Basic works too (curl -u "$CLIENT_ID:$CLIENT_SECRET"), which is what most SDKs send. No refresh token is issued — the client already holds a long-lived secret and simply re-requests when the hour is up.
3. Use it:
curl -s -X POST https://pse.sakayandgo.com/mcp \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0"}}}'Revoke with pse-edge-admin revoke-machine-client <client_id>, which kills the secret, every token it minted, and the backing service account in one step.
Registering does not grant this.
/oauth/registeris open to the internet, so a client that registers itself — even declaringgrant_types: ["client_credentials"]and sending a secret — is refused withunauthorized_client. Authorization comes from aclient_typecolumn only the admin CLI writes, never from anything a registrant says about itself.
Give each agent its own machine client: quotas are per client, so a runaway job throttles itself, and revoking one does not touch the others.
Building an app on top of this? examples/langgraph_client.py is a working client for the multi-tenant case — your app authenticates as itself with one machine client, your users never see this server. It carries an httpx.Auth that mints and refreshes the 1-hour token (verified: concurrent calls mint once; a stale token recovers on 401), plus the agent instructions worth pasting into a system prompt. Note it needs mcp<2 — langchain-mcp-adapters does not yet import against the 2.x SDK.
If your client does not do OAuth yet
The operator issues a token directly, and the user pastes it into a header. Same server, no browser:
pse-edge-admin create-user you@example.com
pse-edge-admin issue-token you@example.com --note laptop # plaintext shown oncecurl -X POST https://your-host.example.com/mcp \
-H "Authorization: Bearer pse_..." \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'This is also the only route on a LAN-only deployment: passkeys need a secure context, so plain http cannot enroll one.
What a user can see and remove
/account shows everything held about them — email, passkeys, active tokens, hourly usage counts. POST /account/delete erases it immediately and completely, with no approval step. /privacy states what is collected and for how long. Usage counts are deleted after 90 days.
Run with Docker Compose (HTTP + Postgres)
cp .env.example .env # set POSTGRES_PASSWORD
docker compose up --buildServes streamable HTTP on :8000, with Postgres 18 as shared cache and archive. A one-shot migrate service applies the Alembic schema before the app starts.
HTTP mode is stateless with plain JSON responses by default. This server is read-only tools over data the freeze policy holds still, and it uses none of the features MCP sessions exist to enable — no notifications, no resource subscriptions, no sampling, no elicitation, no progress — so every request is self-contained. That means any replica can serve any request behind plain round-robin: no sticky routing, no per-session memory, no event store. Without SSE, idle clients hold no connection either. Use --stateful if you need MCP sessions and --sse for event-stream framing; they are independent flags.
Bearer auth and quotas (opt-in)
Set PSE_AUTH_REQUIRED=1 (needs DATABASE_URL) and every HTTP request must carry Authorization: Bearer <token>. Users arrive either way described in Connecting to a hosted server — self-service through OAuth 2.1 and passkeys, or an operator-issued token. PKCE is mandatory (S256 only) and no password exists anywhere in the system.
Tokens are opaque and stored only as SHA-256 hashes. Revocation (pse-edge-admin revoke-token … / disable-user …) takes effect within the validation cache's TTL — 60 s by default, which is precisely the revocation-latency budget. Per-user quotas (default 60/min, 2,000/day, overridable per user) are counted in-process and answer HTTP 429 with Retry-After; with N replicas the effective ceiling is up to N× nominal, which is fine for abuse prevention. stdio mode never authenticates — it runs on your own machine.
Operators get pse-edge-admin delete-user and purge-usage (cron the latter daily), and delete-user uses the same erasure code path as the user's own delete button, so the two cannot drift apart.
Postgres is optional. Without DATABASE_URL the server uses an in-memory cache and keeps no archive — the zero-config path for local stdio use. With it set, replicas share one cache (the freeze still means one upstream fetch per boundary however many processes run), and every read accumulates into an EOD archive (daily bars and disclosures) that deepens over time at zero extra cost to PSE Edge. Nothing crawls — the archive fills solely from fetches you already made.
# applying the schema by hand, outside compose
DATABASE_URL=postgresql+asyncpg://user:pass@host/db uv run alembic upgrade headConfiguration
Everything is environment-sourced into one frozen Settings object. Two variables change the shape of the system: DATABASE_URL picks the storage column, and PSE_AUTH_REQUIRED turns on the whole auth stack (and the send_email tool with it).
Variable | Default | What it governs |
Upstream — protect PSE Edge | ||
|
| Upstream portal root |
|
| Token-bucket rate toward Edge |
|
| Per-request timeout and retries |
Storage — the one switch | ||
| unset | Unset → in-memory cache + |
|
| Connection pool |
Auth — opt-in, needs | ||
|
| Bearer auth + quotas + OAuth/passkeys; stdio never authenticates |
|
| The revocation-latency budget — nothing else |
|
| Per-user quotas, counted in-process (per worker) |
|
| Real external https URL — drives WebAuthn rp_id, email links, OAuth issuer; a wrong value breaks passkeys |
|
| Token lifetimes; the refresh token is single-use and reuse revokes the family. |
| empty | Operator allowlist for the |
Email & operations | ||
| unset | Unset → |
|
| Sender address (ZeptoMail verifies exact domains) |
| unset | Canary failure alerts — failures only, never "all fine" |
|
| Usage log retention (aggregated per user-hour, never per request) |
Server | ||
|
| MCP session & response mode |
|
| Both formatters timestamp and redact; INFO logs refusals only |
Container image
Every merge to main publishes an image:
docker pull ghcr.io/phdwight/pse-edge-mcp:latest # or :<version>, :sha-<sha>
# multi-arch: linux/amd64 and linux/arm64
docker run --rm -p 8000:8000 ghcr.io/phdwight/pse-edge-mcp:latest # streamable HTTP
docker run --rm -i --entrypoint pse-edge-mcp ghcr.io/phdwight/pse-edge-mcp:latest # stdioBoth architectures are gated before publishing, on native runners. The rule is necessity, not size: the image must contain exactly the resolved runtime dependency closure and nothing else — no build toolchain, no package manager, no dev dependencies, no bytecode caches, no source tree — plus a secret scan and a smoke test that the server starts and registers its tools. A stray dependency fails the build; a large but genuinely required one does not. Image size is reported for information and never gated.
Production
One file, compose.nas.yaml, for a NAS or any single Docker host, in two stages. It pulls the published image rather than building, so production runs the artifact CI gated. Stage 1 is LAN-only and needs nothing from Cloudflare:
docker compose -f compose.nas.yaml up -d # http://<nas-ip>:8200
docker compose -f compose.nas.yaml --profile tunnel up -d # + public hostnameThe tunnel profile starts cloudflared, which dials out — so there is no port forwarding, no ACME, and nothing for CGNAT to break; Cloudflare terminates TLS at its edge. Set CLOUDFLARE_TUNNEL_TOKEN, PSE_PUBLIC_URL and PSE_LAN_BIND=127.0.0.1 in .env alongside it — the last moves the stage 1 LAN port onto loopback, which is the only way to unpublish it, because Compose merges ports additively.
Both stages give auth on by default, daily backups, a daily retention purge, and no published database port. Health probes are /health (liveness) and /health/ready (readiness). The app is importable for other servers: uvicorn pse_edge_mcp.asgi:app --workers 4.
See docs/deploy.md for the full guide, including the two settings most worth getting right: pin PSE_IMAGE_TAG rather than tracking :latest, and make PSE_PUBLIC_URL the real external https URL, because WebAuthn binds every passkey to the origin it was enrolled under.
Development
uv sync --all-extras
uv run pytest
uv run ruff check .Tests run entirely against recorded fixtures — CI never touches PSE Edge.
New to the codebase? docs/walkthrough.md is the developer and architect walkthrough: the request lifecycle, the freeze policy, the layering, how to add a tool or a whole data domain, and a symptom-to-cause debugging table. Also available as a PDF. For the one-page visual version of the Architecture section — classes, protocols, the data path, the config matrix — open docs/reference-card.html in any browser; it is fully self-contained and works offline. Every design decision is recorded in docs/plan.md, and the verified endpoint map lives in docs/endpoints.md.
Contributing
Issues and pull requests are welcome. The ground rules:
Work lands on
developand reachesmainby pull request;mainis protected and requires all three CI checks (test,image (amd64),image (arm64)).Tests never touch PSE Edge — new endpoints need new recorded fixtures in
tests/fixtures/.New tools follow the layering above: a new data domain is a new repository plus thin tools, never fetch/parse logic in
server.py.Bumping
versioninpyproject.tomlmakes the next merge cut a GitHub Release with a matching immutable image tag; rollCHANGELOG.mdin the same PR.
License
MIT
MCP Registry identity: mcp-name: io.github.phdwight/pse-edge-mcp
Available Tools
13 toolsget_company_profileCompany profileARead-onlyInspect
Get a PSE-listed company's profile: sector, incorporation, auditor, contacts.
Includes sector and subsector, incorporation date, corporate life, number of directors, fiscal year end, stockholders' meeting schedule, external auditor, transfer agent, business address, phone, fax, email and website. Every label on the page is also returned verbatim in raw_fields.
| Name | Required | Description | Default |
|---|---|---|---|
| symbol | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safe read-only nature is known. The description adds valuable behavioral detail: 'Every label on the page is also returned verbatim in raw_fields', which is not in the schema and gives insight into the output structure.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description opens with a clear, front-loaded summary sentence, followed by a detailed field list and the raw_fields note. It is informative without redundant fluff, though the field enumeration is slightly lengthy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a read-only, single-parameter tool with an output schema, the description is sufficiently complete. It covers the scope (PSE-listed), returns a comprehensive field list, and highlights the raw_fields behavior. No critical usage context is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has 0% description coverage for the single parameter 'symbol'. The description compensates partially by mentioning 'PSE-listed company', implying symbol refers to a PSE ticker, but it gives no format, examples, or validation guidance, leaving some ambiguity.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Get a PSE-listed company's profile' and enumerates the specific contents (sector, incorporation, auditor, contacts). This distinguishes it from sibling tools like get_stock_quote and get_financial_highlights, making the purpose unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Clear context: the tool is for retrieving a company profile for PSE-listed stocks. It does not explicitly state when not to use it or point to alternatives, but the sibling tool names and the clarity of purpose make the appropriate usage obvious.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_disclosureDisclosure detailARead-onlyInspect
Get one disclosure's details and attachment links by its edge_no.
edge_no is the 32-character hex id returned by search_disclosures. Returns the company, template, date, related documents, and URLs for each attachment plus the rendered body HTML. To read a file's contents, use the attachment's resource_uri (pse-edge://attachment/) via resources/read — the tools themselves stay metadata-only; download_url remains for callers that can fetch URLs directly.
At most max_files attachments are returned (default 20, max 100). attachments_total always reports how many exist; if attachments_truncated is true, call again with a higher max_files — the disclosure is cached, so the repeat costs nothing upstream.
A published disclosure never changes, so these results are cached permanently (meta.data_policy is "immutable").
| Name | Required | Description | Default |
|---|---|---|---|
| edge_no | Yes | ||
| max_files | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses that published disclosures are immutable and cached permanently, that the tool is metadata-only (no file bytes), and that repeated calls cost nothing upstream. This significantly enriches the behavioral model.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded with a clear purpose sentence, then logically progresses to parameter semantics, usage alternatives, and caching behavior. Every sentence adds value; there is no fluff or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
It covers the input provenance, return content, pagination, caching, immutability, and a clear note that file content requires resources/read. Even with an output schema present, this description leaves no relevant gap for an AI agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero description coverage, but the description defines edge_no as the 32-character hex ID from search_disclosures and explains max_files with default (20) and max (100), plus the truncation behavior. This fully compensates for the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description starts with a specific verb+resource: 'Get one disclosure's details and attachment links by its edge_no.' It clearly scopes the tool to retrieving a single disclosure by ID, which distinguishes it from the search siblings (e.g., search_disclosures, search_disclosure_fulltext).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly tells the caller where to obtain edge_no (from search_disclosures) and when to use resources/read instead for file contents. It also gives guidance on handling truncation by increasing max_files, which is actionable usage context.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_dividends_and_rightsDividends & rightsARead-onlyInspect
Get a company's declared dividends and stock rights offers.
Dividends carry the security and dividend type, the rate as printed, and the
ex-dividend, record and payment dates. Rights carry the entitlement ratio, offer
price, ex-rights date and offer period. Each record includes the edge_no of the
disclosure that announced it, so you can pass it to get_disclosure for the notice
itself. Empty lists mean Edge lists none for this company.
| Name | Required | Description | Default |
|---|---|---|---|
| symbol | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false. The description adds valuable behavioral context by explaining that empty lists mean Edge lists none for this company, and that each record includes an edge_no linking to the announcement disclosure. This goes beyond the annotations and does not contradict them.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single focused paragraph, front-loaded with the core purpose. Each subsequent detail—field lists, edge_no linkage, empty-list interpretation—adds value without redundancy. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's simplicity (one parameter, output schema present, readOnly, openWorld false), the description covers all relevant aspects: what data is returned, how to link to the disclosure, and how to interpret empty results. This is sufficient for an agent to correctly invoke and understand the tool's output.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has a single parameter 'symbol' with no description, and schema description coverage is 0%. The description only says 'a company's' and does not explain the symbol format, validation, or any additional semantics, leaving the parameter under-specified and requiring the agent to infer what 'symbol' means from the context.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with 'Get a company's declared dividends and stock rights offers,' using a specific verb and resource that clearly distinguishes it from sibling tools like get_stock_quote or get_disclosure. It further elaborates on the fields included for dividends and rights, making the tool's scope unmistakable.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies when to use this tool by stating it retrieves dividend and rights data. It also provides a clear usage tip: pass the returned edge_no to get_disclosure for the full notice. However, it doesn't explicitly mention alternatives or when not to use the tool, so it falls short of a 5.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_financial_highlightsFinancial highlightsARead-onlyInspect
Get the financial highlights PSE Edge publishes for a company.
Returns an annual and a quarterly section, each with a balance sheet and an income statement, using Edge's own line-item labels and column headings (annual compares Current/Previous Year; quarterly compares Period Ended against the audited fiscal year, and its income statement has four columns including year-to-date).
IMPORTANT — units: figures are returned exactly as Edge prints them and are never
rescaled. Each period carries its own currency_units label, and the two sections
disagree in practice (observed: annual "Php (in thousands)" while quarterly said
"Php (in Millions)" for the same company, with the same figure appearing in both).
Read currency_units before quoting any number, and say the scale is uncertain
rather than presenting these as exact peso amounts. Only the highlights Edge
serves as data are here — this server does not parse filed PDF statements.
| Name | Required | Description | Default |
|---|---|---|---|
| symbol | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the readOnlyHint annotation by disclosing critical behavioral details: figures are never rescaled, each period has its own currency_units label, the two sections may disagree (with a concrete observed example), and the scale should be treated as uncertain if labels conflict. This is exactly the kind of contextual information an agent needs to avoid misinterpreting results.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is efficiently structured: purpose first, then return structure, then the critical units warning, then scope clarification. Each sentence earns its place, and the units warning is front-loaded for safety. Despite being relatively long, it is highly informative and not verbose.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (annual vs quarterly sections, currency unit inconsistencies, line-item labels), the description covers the key aspects of return format, data source limitations, and the critical unit caveat. Since an output schema exists, return values are further specified, making the description complete for an agent to invoke the tool correctly and interpret results safely.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema has only one parameter 'symbol' with no description. The tool description does not explain the symbol parameter's format or examples, despite low schema coverage (0%). The phrase 'for a company' indirectly implies the parameter is a company identifier, but it does not compensate for the lack of explicit parameter documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Get') and resource ('financial highlights PSE Edge publishes for a company'), and details the return structure (annual/quarterly sections with balance sheet and income statement). This clearly distinguishes it from siblings like get_stock_quote or get_company_profile.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives clear context about data scope and an important exclusion ('Only the highlights Edge serves as data are here — this server does not parse filed PDF statements'). This implicitly tells when not to use the tool, but it does not name alternative tools explicitly, leaving some ambiguity for agents unfamiliar with sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_indicesMarket indicesARead-onlyInspect
Get PSEi and the PSE sector index levels with their daily change.
Covers PSEi, All Shares, Financials, Industrial, Holding Firms, Property, Services
and Mining and Oil. change and change_percent are signed, and direction is
"up"/"down"/"flat" — PSE Edge prints these unsigned and shows direction only as a
colour and an arrow, so the signs here are derived from that. Fetched at most once
per boundary window and served from storage until the next 15:00 Manila close —
meta.as_of says when the snapshot was taken.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already signal read-only (readOnlyHint=true) and deterministic (openWorldHint=false), but the description adds significant behavioral context: the sign conventions (change and change_percent are signed, direction is up/down/flat derived from PSE Edge's color/arrow), the caching policy (at most once per boundary window), and the meta.as_of field for snapshot timing. This substantially goes beyond the annotations, delivering high transparency.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is moderately sized (4 sentences) and front-loaded with the main purpose. Each subsequent sentence adds necessary detail (coverage, sign semantics, caching) without fluff. It could be slightly more compact, but it remains efficient and relevant, earning a 4 rather than a 5.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no parameters and an output schema present, the description covers all essential contextual aspects: what data is returned, which indices are included, the interpretation of direction fields, and the staleness/caching behavior. The existence of an output schema handles return field details, so the description is complete for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, and the schema coverage is 100% (vacuously). The baseline for zero-parameter tools is 4 per the rubric. The description adds no parameter info (not needed), so the score reflects the absence of complexity.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool retrieves PSEi and PSE sector index levels with daily change. The verb 'Get' and specific resource 'PSEi and sector index levels' is explicit, and it naturally distinguishes from siblings like get_stock_quote (single stock) and get_market_summary (broader market metrics).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description clearly indicates the scope (which indices are covered) and the context (cached until 15:00 Manila close), which helps decide when to use it. However, it does not explicitly mention when not to use it or suggest alternatives, so it falls short of a 'when/when-not' distinction. Still, the clarity of purpose provides strong implicit guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_market_summaryMarket summaryARead-onlyInspect
Get a market-wide snapshot: index levels plus PSE Edge's homepage feeds.
feeds is keyed by Edge's own group labels — Company Announcements, Financial
Reports, Other Reports, Listing Notices, Disclosure Notices, and the most-viewed
disclosures for Today and This Week. Each entry carries its symbol, timestamp,
circular number and edge_no for get_disclosure.
Note: PSE Edge publishes no gainers/losers/most-active data anywhere, so this cannot include them — say so rather than implying the data is missing or stale.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true and openWorldHint=false, but the description adds substantial context: how feeds are keyed (group labels), what each entry carries (symbol, timestamp, circular number, edge_no), and a critical limitation (PSE Edge publishes no gainers/losers/most-active data anywhere). This goes beyond the structured annotations and helps the agent interpret results and avoid misstatements.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The first sentence is a concise summary. The second paragraph lists the feed keys and their fields, which is necessary detail given the tool's output scope. The limitation note is important and earns its place. Slightly dense but well-organized; no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the output schema exists, the description doesn't need to list return fields, but it still provides essential context: feed structure, key labels, and the data limitation. With zero parameters and a rich output description, an agent has enough to invoke the tool and interpret results without ambiguity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema has zero parameters, and the description itself doesn't mention parameters, which is appropriate. With no parameters, the schema coverage is trivially 100% and the baseline is 4. The description adds no param semantics because none exist.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb ('Get') and resource ('market-wide snapshot') with concrete scope: index levels plus PSE Edge's homepage feeds. This clearly differentiates it from siblings like get_indices (indices only) and search_disclosures (disclosure search), so purpose is unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context for when to use this tool (for a market-wide snapshot) and includes an important behavioral instruction: if asked for gainers/losers, explicitly state PSE Edge doesn't publish them rather than implying missing data. It doesn't explicitly name alternatives or exclusions, but the context is enough for an agent to select this tool appropriately.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_price_historyPrice history (OHLC)ARead-onlyInspect
Get daily OHLC price history for a PSE stock symbol.
Dates are ISO format (YYYY-MM-DD). Defaults to the last ~6 months. Data comes from PSE Edge's own chart endpoint and is EOD-frozen. If the market is open and this exact range has never been cached, a one-time fetch may include today's still-moving bar, flagged stale=true with an explanatory meta.note.
| Name | Required | Description | Default |
|---|---|---|---|
| symbol | Yes | ||
| end_date | No | ||
| start_date | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description adds valuable behavioral details: the data is EOD-frozen from PSE Edge's chart endpoint, and a one-time fetch may include a live bar flagged stale=true with an explanatory meta.note. This discloses caching and freshness behavior not captured by annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: a one-sentence purpose, followed by a terse paragraph of relevant details. Every sentence adds value—date format, default window, data source, staleness caveat—with no redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The tool has an output schema and simple parameters; the description covers the essential context: data source, time zone or date format, default window, and the stale-bar edge case. This is complete for a read-only historical data tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage for parameters is 0%, but the description compensates by documenting date format (ISO YYYY-MM-DD), default range (~6 months), and symbol identity (PSE stock symbol). This gives the agent meaning for all three parameters beyond the bare schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb-resource statement: 'Get daily OHLC price history for a PSE stock symbol.' This clearly identifies the tool's function and scope, and differentiates it from sibling tools like get_stock_quote (current quote) and search_companies.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description establishes clear usage context: daily OHLC historical data for PSE symbols, with default date range and ISO format. It does not explicitly name alternative tools or state when not to use it, but the context is sufficient for an agent to select it for historical OHLC requests.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_server_versionServer versionARead-onlyInspect
Get the deployed version of this MCP server (pse-edge-mcp).
Reports the running server's own release version — the same value the
/health endpoint and serverInfo carry — not PSE Edge data. There is no
meta block: meta is a data-freshness contract, and a version has no
as_of or valid_until.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description explains a subtle behavioral detail: there is no meta block because meta is a data-freshness contract and a version has no as_of or valid_until. This adds valuable context not available from annotations alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded, with the primary purpose in the first sentence. The subsequent sentences add relevant detail about what the version refers to and the absence of a meta block, with no redundancy or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple, parameterless tool with an output schema and safe read-only annotations, the description fully covers the tool's purpose, scope, and return behavior. It even explains why a typical data-freshness field is absent, making it complete for an agent.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The tool has zero parameters, and the schema is empty (100% coverage). The description correctly notes the absence of a meta block but doesn't need to explain parameters since there are none. Baseline for zero parameters is 4, and this description adequately addresses the tool's scope.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool's function: 'Get the deployed version of this MCP server (pse-edge-mcp).' It explicitly distinguishes itself from sibling data tools by noting it returns the server's own release version, not PSE Edge data.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description provides clear context for when to use this tool: when you need the server's deployed version. It also clarifies that this is not for PSE Edge data, effectively excluding data-related use cases, and points to alternative sources (health endpoint, serverInfo) for the same value.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_stock_quoteStock quote (EOD)ARead-onlyInspect
Get the latest end-of-day quote for a PSE stock symbol (e.g. SM, AREIT, BDO).
Includes price, change, 52-week range, market cap, shares, and the full set of fields PSE Edge publishes. Data is EOD-frozen (see meta). If the market is open and this symbol has never been cached, a one-time fetch serves identity plus previous_close ONLY (the last settled price before the session — every other field is null), flagged stale=true with a meta.note saying the value is not realtime — relay that caveat when presenting the price.
| Name | Required | Description | Default |
|---|---|---|---|
| symbol | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the readOnlyHint annotation, the description discloses the EOD-frozen nature of the data and details the special case where an uncached symbol during market hours returns only identity plus previous_close with null fields and stale=true. This is substantial additional behavioral context that the annotation alone does not provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact yet covers the core function, included fields, and a critical caching caveat in a single well-organized paragraph. Every sentence adds actionable information without filler or redundant repetition of the schema.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The description fully covers the tool's behavior, including the edge-case stale response and a pointer to the meta field in the output schema. Since an output schema exists, return values are documented, and the description complements it with usage-level detail.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema's symbol parameter has no description (schema description coverage 0%), but the tool description specifies it is a PSE stock symbol and gives valid examples (SM, AREIT, BDO). This compensates for the missing schema description and gives the agent concrete formatting guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a specific verb phrase 'Get the latest end-of-day quote for a PSE stock symbol' and includes concrete examples (SM, AREIT, BDO). This clearly distinguishes it from siblings like get_price_history by focusing on the latest EOD snapshot rather than historical time series.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description implies usage for obtaining current EOD quotes but does not explicitly discuss when to prefer this tool over alternatives like get_price_history. No exclusions or when-not-to-use guidance is provided, leaving the agent to infer the appropriate context from the sibling names.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_companiesSearch companiesARead-onlyInspect
Search PSE-listed companies by name or ticker symbol.
Returns matches with company_id, name, symbol. Use the symbol with the other tools.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint=true, so the safety profile is known. The description adds valuable context by specifying the return fields (company_id, name, symbol) and how to use the output with other tools, going beyond what annotations provide.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loaded with the purpose, then gives output and usage guidance. Every sentence earns its place with no redundant or vague wording.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a simple one-parameter search tool with an output schema, the description covers purpose, input semantics, output structure, and integration with sibling tools. It is complete and leaves no major gaps.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema provides no description for the 'query' parameter, but the description fully defines it as a name or ticker symbol, completely covering the semantics. This compensates for the 0% schema description coverage.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states a specific verb+resource: 'Search PSE-listed companies by name or ticker symbol.' It distinguishes this tool from sibling tools like search_disclosures or get_stock_quote by focusing on company lookup and explicitly mentioning the use of the symbol with other tools.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The phrase 'Use the symbol with the other tools' gives clear context that this is the entry point for obtaining company symbols, but it does not explicitly mention when not to use it or name alternatives such as search_disclosures. It implies usage well but lacks explicit exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_disclosure_fulltextFull-text disclosure searchARead-onlyInspect
Full-text search inside disclosure attachments, returning matching snippets.
Use this to find wording within filings ("share buyback", "force majeure"). For "what did company X disclose recently", use search_disclosures instead.
IMPORTANT: PSE Edge's own full-text index is partial and lags behind — at last verification it covered roughly 2023-2025 and held nothing from 2026. Results are relevance-ordered (not chronological), 10 per page. The result includes a coverage_note; relay that limitation rather than reporting "no disclosures exist".
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | ||
| symbol | No | ||
| keyword | Yes | ||
| end_date | No | ||
| start_date | No | ||
| subject_title | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description discloses critical behavioral traits beyond the readOnlyHint and openWorldHint annotations: the partial index coverage (2023–2025, nothing from 2026), relevance-ordered results, 10 per page, and the inclusion of a coverage_note. It also instructs the agent to relay the limitation, which is rich, actionable context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is appropriately sized and front-loaded with the core function, followed by usage guidance and a key limitation note. Every sentence provides value, and the structure is easy to parse.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (6 params, output schema, annotations), the description covers the essential context: purpose, alternative usage, index limitations, ordering, pagination, and the coverage_note. With an output schema present, the description need not elaborate on return values beyond what is already provided.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, so the description carries the burden. It adds meaning for the main parameter 'keyword' by indicating the search term for wording inside filings, but it does not explain optional parameters like symbol, dates, or subject_title. The schema titles provide minimal semantics, so the description only partially compensates for the lack of parameter documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool performs full-text search inside disclosure attachments and returns matching snippets, distinguishing it from sibling search_disclosures by explicitly noting the alternative use case. The verb 'search' and resource 'disclosure attachments' are specific and unambiguous.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicit usage guidance is given: 'Use this to find wording within filings' and 'For "what did company X disclose recently", use search_disclosures instead.' This directly instructs when to use this tool versus the sibling, satisfying the criteria for alternatives and exclusions.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_disclosuresSearch disclosuresARead-onlyInspect
Search PSE company disclosures (announcements, material information, notices).
Returns disclosure metadata — pass a hit's edge_no to get_disclosure for attachment links. 50 results per page; the result reports total/pages/has_more so you can request the next page directly.
symbol only: that company's full disclosure history, newest first.
date range (ISO YYYY-MM-DD): all companies' disclosures in that window, or one company's if symbol is also given. Defaults to the last 30 days when neither symbol nor dates are supplied.
template: filter by disclosure type as free text, e.g. "Press Release", "Cash Dividend", "Material Information".
A given search hits PSE Edge once and is then served from storage until the next 15:00 Manila close, so a disclosure filed today appears when its query is first asked — or after the close if that query was already cached (see meta).
| Name | Required | Description | Default |
|---|---|---|---|
| page | No | ||
| symbol | No | ||
| end_date | No | ||
| template | No | ||
| start_date | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations declare readOnlyHint and openWorldHint, but the description adds significant behavioral context: caching until next 15:00 Manila close, pagination specifics (total/pages/has_more), and the fact that a query may serve stale data if already cached. No contradiction with annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is appropriately sized, uses bullet points for clarity, and every sentence adds value. It is well-structured and front-loaded with the core purpose, then expands into usage and caching details without redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool's complexity (5 optional parameters, pagination, caching, output schema), the description covers all essential aspects: what it returns, how to paginate, parameter semantics, and behavioral nuances. The output schema exists and the description complements it without needing to explain return values in depth.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 0%, so the description fully compensates by explaining all five parameters: symbol (company's full history), start_date/end_date (ISO YYYY-MM-DD, date range), template (free-text filter), and page (implicitly via 'request the next page directly'). Every parameter is semantically clarified.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states the tool searches PSE company disclosures (announcements, material information, notices), specifies it returns disclosure metadata, and distinguishes it from get_disclosure by noting edge_no is for attachment links. This is a specific verb+resource+scope with clear differentiation from siblings.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides detailed usage patterns: symbol-only, date range, template filter, defaults when neither symbol nor dates are supplied, pagination approach (50 results per page, next page directly), and caching behavior. It also implies alternatives (get_disclosure for attachments) and clarifies when results appear relative to the Manila close.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
validate_symbolValidate ticker symbolARead-onlyInspect
Check whether a ticker symbol is a real PSE-listed company. Cheap.
Use this — NOT search_companies — when you only need to know whether a symbol is
valid before calling another tool, or to confirm a symbol a user typed. Returns
valid true/false plus the company name and id when it exists, instead of the
ranked list of near-matches search_companies returns.
Matching is exact and case-insensitive: "areit" and "AREIT" both resolve, while
"ARE" does not match "AREIT". An unknown symbol is valid: false with null
fields, not an error.
Cached after the first lookup and refreshed daily (see meta).
| Name | Required | Description | Default |
|---|---|---|---|
| symbol | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes well beyond the readOnlyHint annotation by detailing exact matching behavior ('case-insensitive... "areit" and "AREIT" both resolve'), unknown-symbol handling ('valid: false with null fields, not an error'), and caching ('Cached after the first lookup and refreshed daily'). These are non-obvious behaviors an agent needs to know.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact yet information-dense, using short paragraphs and a front-loaded purpose statement. Every sentence adds value—purpose, cost hint, usage alternative, return behavior, matching rules, and caching—with no repetition or filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Despite having an output schema, the description still explains the key return components (valid flag, company name/id, null fields) and error behavior. It also covers caching, performance, and provides differentiation from the most relevant sibling, making it complete for decision-making.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
With 0% schema coverage, the description compensates by defining the sole parameter ('ticker symbol') and illustrating semantics with case-sensitivity examples. It does not provide exhaustive format restrictions, but the examples and matching rules give sufficient meaning for correct use.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description opens with a clear, specific purpose: 'Check whether a ticker symbol is a real PSE-listed company.' It uses a strong verb ('check') and explicitly differentiates from the sibling tool search_companies by contrasting the return type (valid true/false vs. ranked list of near-matches).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage guidance is explicit: 'Use this — NOT search_companies — when you only need to know whether a symbol is valid before calling another tool, or to confirm a symbol a user typed.' It also names the alternative and explains why this tool is preferred, giving clear decision criteria.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
2 tool updates
v0.16.1- Changed
get_disclosure1 field changed- added
Input schema / properties / max_filesAdded value: +{ + "default": 20, + "title": "Max Files", + "type": "integer" +}
- Added
get_server_version
12 tool updates
v0.10.0- First observed
get_company_profile - First observed
get_disclosure - First observed
get_dividends_and_rights - First observed
get_financial_highlights - First observed
get_indices - First observed
get_market_summary - First observed
get_price_history - First observed
get_stock_quote - First observed
search_companies - First observed
search_disclosure_fulltext - First observed
search_disclosures - First observed
validate_symbol
TDQS
Each tool targets a distinct resource and action: search vs. validate, quote vs. history, disclosure search vs. full-text search, and separate tools for profile, financials, dividends, indices, and market summary. There is no overlap in purpose; even similar tools like validate_symbol and search_companies are explicitly differentiated in descriptions.
All tools follow a consistent verb_noun snake_case pattern: search_, get_, validate_. The verbs are predictable and the nouns clearly describe the target resource. No mixed conventions or vague names.
13 tools is well within the ideal 3-15 range for a domain-specific data server. Each tool addresses a distinct aspect of PSE data (company lookup, quotes, history, disclosures, financials, dividends, indices, market summary), and none feel redundant or unnecessary.
The tool surface comprehensively covers the PSE Edge data domain: company identification, validation, pricing data, price history, disclosure search (metadata and full-text), disclosure retrieval, company profile, financial highlights, dividends/rights, index levels, and a market-wide summary. Workflows from search to detail retrieval are complete, and the only non-data tool (get_server_version) is a standard meta operation.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Finnhub MCP — wraps Finnhub Stock API (finnhub.io)
EODHD MCP — wraps EOD Historical Data (eodhd.com)
Banxico MCP — Banco de México (Mexico's central bank) via the SIE API.
Taiwan Stock Exchange (TWSE) open data as MCP tools: stock quotes, ETF data, 140+ public datasets.
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceA lightweight MCP server for accessing Yahoo Finance data, providing stock prices, history, company information, and financial statements.-
- AlicenseBqualityCmaintenanceProvides tools to access Vietnam stock market data, including stock prices, financial statements, and market statistics.361MIT
- FlicenseNot gradedqualityCmaintenanceProvides live US stock quotes and historical price data via MCP tools.-
- AlicenseAqualityAmaintenanceRead-only MCP server for Stockbit (IDX market data) providing broker summary/bandarmology, quotes, top movers, orderbook, fundamentals, and sentiment using your own session. Unofficial and does not place orders.84171023MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/phdwight/pse-edge-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server