universalis-mcp-server 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -2
- package/data/materia.json +1 -1
- package/dist/constants.js +2 -0
- package/dist/index.js +5 -0
- package/dist/instructions.js +27 -1
- package/dist/services/clients.js +13 -1
- package/dist/services/saddlebag.js +25 -0
- package/dist/tools/saddlebag.js +682 -0
- package/dist/tools/wiki.js +176 -0
- package/docs/wiki/index.json +153 -0
- package/docs/wiki/saddlebag/allagan-tools-inventory-analysis.md +94 -0
- package/docs/wiki/saddlebag/api-call-guide.md +173 -0
- package/docs/wiki/saddlebag/ffxiv-advanced-undercut-alert-options.md +123 -0
- package/docs/wiki/saddlebag/ffxiv-experimental-discount-price-sniper.md +7 -0
- package/docs/wiki/saddlebag/ffxiv-job-category-ids.md +13 -0
- package/docs/wiki/saddlebag/ffxiv-sale-alerts.md +48 -0
- package/docs/wiki/saddlebag/ffxiv-sale-leads.md +42 -0
- package/docs/wiki/saddlebag/how-to-trade-using-our-ffxiv-market-overview.md +102 -0
- package/docs/wiki/saddlebag/how-to-use-the-ffxiv-crafting-profit-simulator-craftsim.md +88 -0
- package/docs/wiki/saddlebag/item-categories-ids-and-list.md +217 -0
- package/docs/wiki/saddlebag/tldr-how-to-earn-gil-with-cross-server-trading.md +89 -0
- package/docs/wiki/universalis/api-app-overview.md +236 -0
- package/docs/wiki/universalis/how-to-help-update-the-data-on-universalis.md +21 -0
- package/docs/wiki/universalis/how-to-make-universalis-lists.md +24 -0
- package/docs/wiki/universalis/how-to-search-for-items-on-universalis-and-saddlebag-exchange.md +56 -0
- package/docs/wiki/universalis/how-to-setup-universalis-alerts.md +54 -0
- package/docs/wiki/universalis/how-to-use-universalis-favorites.md +17 -0
- package/docs/wiki/universalis/interactive-tutorials.md +3 -0
- package/docs/wiki/universalis/navigating-an-item-overview-on-universalis.md +72 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# universalis-mcp-server
|
|
2
2
|
|
|
3
|
-
MCP server for
|
|
3
|
+
MCP server for FFXIV market data via Universalis + Saddlebag Exchange, plus XIVAPI item lookup.
|
|
4
4
|
|
|
5
5
|
## Capabilities
|
|
6
6
|
|
|
7
|
-
This MCP server exposes read-only tools for Universalis market data and XIVAPI item lookup.
|
|
7
|
+
This MCP server exposes read-only tools for Universalis market data, Saddlebag Exchange analytics, and XIVAPI item lookup.
|
|
8
8
|
All tools support `response_format` as `markdown` or `json`.
|
|
9
9
|
|
|
10
10
|
### Market board data (Universalis)
|
|
@@ -24,6 +24,30 @@ All tools support `response_format` as `markdown` or `json`.
|
|
|
24
24
|
|
|
25
25
|
- `universalis_rank_items_by_profitability`: Resolve names, fetch aggregated data, and rank by demand and profit.
|
|
26
26
|
|
|
27
|
+
### Market analytics (Saddlebag Exchange)
|
|
28
|
+
|
|
29
|
+
- `saddlebag_get_listing_metrics`: Listing competition metrics and current listings.
|
|
30
|
+
- `saddlebag_get_history_metrics`: Aggregated history metrics and price distributions.
|
|
31
|
+
- `saddlebag_get_raw_stats`: Daily snapshot stats (median, average, sales volume).
|
|
32
|
+
- `saddlebag_get_scrip_exchange`: Gil-per-scrip rankings.
|
|
33
|
+
- `saddlebag_get_shopping_list`: Crafting shopping list across servers (may return exception payloads).
|
|
34
|
+
- `saddlebag_get_export_prices`: Cross-world price comparison for export trading.
|
|
35
|
+
- `saddlebag_get_marketshare`: Marketshare leaderboard snapshot for a server.
|
|
36
|
+
- `saddlebag_get_reselling_scan`: Reselling opportunities (region/DC/vendor filters).
|
|
37
|
+
- `saddlebag_get_weekly_price_group_delta`: Weekly deltas for custom item groups.
|
|
38
|
+
- `saddlebag_get_price_check`: Price alert checks (user-supplied auctions).
|
|
39
|
+
- `saddlebag_get_quantity_check`: Quantity alert checks (user-supplied auctions).
|
|
40
|
+
- `saddlebag_get_sale_alert`: Sale alerts for retainer listings.
|
|
41
|
+
- `saddlebag_get_craftsim`: Crafting profitability scan (use `max_results` to limit payloads).
|
|
42
|
+
- `saddlebag_get_blog_description`: Item description text lookup.
|
|
43
|
+
|
|
44
|
+
Best-deals is excluded (premium gated + inconsistent API validation).
|
|
45
|
+
|
|
46
|
+
### Guides (Wiki snapshots)
|
|
47
|
+
|
|
48
|
+
- `wiki_list_pages`: List curated wiki pages for Saddlebag and Universalis guides.
|
|
49
|
+
- `wiki_get_page`: Fetch a wiki page by slug for extra context.
|
|
50
|
+
|
|
27
51
|
### Reference data (Universalis)
|
|
28
52
|
|
|
29
53
|
- `universalis_list_worlds`: List worlds with pagination.
|
|
@@ -71,6 +95,12 @@ pnpm install
|
|
|
71
95
|
pnpm build
|
|
72
96
|
```
|
|
73
97
|
|
|
98
|
+
To refresh curated wiki snapshots:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pnpm update-wiki-docs
|
|
102
|
+
```
|
|
103
|
+
|
|
74
104
|
## Run (stdio)
|
|
75
105
|
|
|
76
106
|
```bash
|
|
@@ -97,9 +127,13 @@ If you change code, re-run `pnpm build` and restart the MCP connection.
|
|
|
97
127
|
|
|
98
128
|
- `UNIVERSALIS_BASE_URL`: Override Universalis base URL (default: `https://universalis.app/api/v2`).
|
|
99
129
|
- `XIVAPI_BASE_URL`: Override XIVAPI base URL (default: `https://v2.xivapi.com/api`).
|
|
130
|
+
- `SADDLEBAG_BASE_URL`: Override Saddlebag base URL (default: `https://docs.saddlebagexchange.com/api`).
|
|
100
131
|
- `UNIVERSALIS_MCP_USER_AGENT`: Custom User-Agent header.
|
|
101
132
|
- `UNIVERSALIS_TIMEOUT_MS`: Request timeout for Universalis (default: 30000).
|
|
102
133
|
- `XIVAPI_TIMEOUT_MS`: Request timeout for XIVAPI (default: 30000).
|
|
134
|
+
- `SADDLEBAG_TIMEOUT_MS`: Request timeout for Saddlebag (default: 30000).
|
|
135
|
+
- `WIKI_FETCH_MODE`: `local` (default) for snapshot files or `live` to fetch from GitHub.
|
|
136
|
+
- `WIKI_REFRESH_TTL_MS`: Cache TTL for live wiki fetches (default: 3600000).
|
|
103
137
|
- `XIVAPI_LANGUAGE`: Default XIVAPI language (default: `en`).
|
|
104
138
|
- `XIVAPI_VERSION`: Default XIVAPI version (default: `latest`).
|
|
105
139
|
|
|
@@ -107,3 +141,6 @@ If you change code, re-run `pnpm build` and restart the MCP connection.
|
|
|
107
141
|
|
|
108
142
|
- Rate limits are enforced client-side for Universalis and XIVAPI.
|
|
109
143
|
- Tools support `response_format` as `markdown` or `json`.
|
|
144
|
+
- Some Saddlebag endpoints proxy Universalis data; avoid excessive polling.
|
|
145
|
+
- Wiki pages are curated snapshots stored in `docs/wiki`.
|
|
146
|
+
- Set `WIKI_FETCH_MODE=live` to refresh wiki pages at runtime using the allowlisted index.
|
package/data/materia.json
CHANGED
package/dist/constants.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export const UNIVERSALIS_BASE_URL = process.env.UNIVERSALIS_BASE_URL ?? "https://universalis.app/api/v2";
|
|
2
2
|
export const XIVAPI_BASE_URL = process.env.XIVAPI_BASE_URL ?? "https://v2.xivapi.com/api";
|
|
3
|
+
export const SADDLEBAG_BASE_URL = process.env.SADDLEBAG_BASE_URL ?? "https://docs.saddlebagexchange.com/api";
|
|
3
4
|
export const DEFAULT_UNIVERSALIS_TIMEOUT_MS = 30000;
|
|
4
5
|
export const DEFAULT_XIVAPI_TIMEOUT_MS = 30000;
|
|
6
|
+
export const DEFAULT_SADDLEBAG_TIMEOUT_MS = 30000;
|
|
5
7
|
export const DEFAULT_TIMEOUT_MS = 30000;
|
|
6
8
|
export const DEFAULT_MATERIA_CACHE_TTL_MS = 1000 * 60 * 60 * 24;
|
|
7
9
|
export const CHARACTER_LIMIT = 25000;
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,9 @@ import { createClients } from "./services/clients.js";
|
|
|
7
7
|
import { registerLookupTools } from "./tools/lookup.js";
|
|
8
8
|
import { registerMarketTools } from "./tools/market.js";
|
|
9
9
|
import { registerReferenceTools } from "./tools/reference.js";
|
|
10
|
+
import { registerSaddlebagTools } from "./tools/saddlebag.js";
|
|
10
11
|
import { registerStatsTools } from "./tools/stats.js";
|
|
12
|
+
import { registerWikiTools } from "./tools/wiki.js";
|
|
11
13
|
import { registerWorkflowTools } from "./tools/workflows.js";
|
|
12
14
|
function toNumber(value) {
|
|
13
15
|
if (!value)
|
|
@@ -27,6 +29,7 @@ async function main() {
|
|
|
27
29
|
userAgent,
|
|
28
30
|
universalisTimeoutMs: toNumber(process.env.UNIVERSALIS_TIMEOUT_MS),
|
|
29
31
|
xivapiTimeoutMs: toNumber(process.env.XIVAPI_TIMEOUT_MS),
|
|
32
|
+
saddlebagTimeoutMs: toNumber(process.env.SADDLEBAG_TIMEOUT_MS),
|
|
30
33
|
xivapiLanguage: process.env.XIVAPI_LANGUAGE,
|
|
31
34
|
xivapiVersion: process.env.XIVAPI_VERSION,
|
|
32
35
|
});
|
|
@@ -34,6 +37,8 @@ async function main() {
|
|
|
34
37
|
registerReferenceTools(server, clients);
|
|
35
38
|
registerStatsTools(server, clients);
|
|
36
39
|
registerLookupTools(server, clients);
|
|
40
|
+
registerSaddlebagTools(server, clients);
|
|
41
|
+
registerWikiTools(server);
|
|
37
42
|
registerWorkflowTools(server, clients);
|
|
38
43
|
server.registerPrompt("universalis_usage_guide", {
|
|
39
44
|
title: "Universalis Usage Guide",
|
package/dist/instructions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const SERVER_INSTRUCTIONS = `Use this server to analyze FFXIV market data via Universalis and item data via XIVAPI.
|
|
1
|
+
export const SERVER_INSTRUCTIONS = `Use this server to analyze FFXIV market data via Universalis and Saddlebag Exchange, plus item data via XIVAPI.
|
|
2
2
|
|
|
3
3
|
Tool guide:
|
|
4
4
|
- universalis_rank_items_by_profitability: best for "most profitable to farm" questions with cost inputs; ranks by demand and profit (defaults to best HQ/NQ price, can filter unmarketable items, and includes supply metrics).
|
|
@@ -25,6 +25,26 @@ Tool guide:
|
|
|
25
25
|
- universalis_get_upload_counts_by_world: upload counts by world.
|
|
26
26
|
- universalis_get_upload_history: daily upload totals for the last 30 days.
|
|
27
27
|
|
|
28
|
+
Saddlebag tools (FFXIV):
|
|
29
|
+
- saddlebag_get_listing_metrics: listing competition metrics and current listings.
|
|
30
|
+
- saddlebag_get_history_metrics: aggregated history metrics and price distributions.
|
|
31
|
+
- saddlebag_get_raw_stats: daily snapshot stats (median, average, sales volume).
|
|
32
|
+
- saddlebag_get_scrip_exchange: gil-per-scrip rankings.
|
|
33
|
+
- saddlebag_get_shopping_list: crafting shopping list (may return exception payloads).
|
|
34
|
+
- saddlebag_get_export_prices: cross-world price comparison for export trading.
|
|
35
|
+
- saddlebag_get_marketshare: marketshare leaderboard snapshot for a server.
|
|
36
|
+
- saddlebag_get_reselling_scan: reselling opportunities (region/DC/vendor filters).
|
|
37
|
+
- saddlebag_get_weekly_price_group_delta: weekly deltas for custom item groups.
|
|
38
|
+
- saddlebag_get_price_check: price alert checks (user-supplied auctions).
|
|
39
|
+
- saddlebag_get_quantity_check: quantity alert checks (user-supplied auctions).
|
|
40
|
+
- saddlebag_get_sale_alert: sale alerts for retainer listings.
|
|
41
|
+
- saddlebag_get_craftsim: crafting profitability scan (use max_results to limit payloads).
|
|
42
|
+
- saddlebag_get_blog_description: item description text lookup.
|
|
43
|
+
|
|
44
|
+
Wiki tools:
|
|
45
|
+
- wiki_list_pages: list curated Saddlebag/Universalis guide pages.
|
|
46
|
+
- wiki_get_page: fetch a wiki page by slug for extra context.
|
|
47
|
+
|
|
28
48
|
Notes:
|
|
29
49
|
- world_dc_region accepts a world name/ID, data center name, or region.
|
|
30
50
|
- response_format defaults to markdown; use json for structured processing.
|
|
@@ -33,4 +53,10 @@ Notes:
|
|
|
33
53
|
- Materia expansion uses cached XIVAPI data; refresh is controlled by MATERIA_CACHE_TTL_MS and MATERIA_REFRESH.
|
|
34
54
|
- Profitability tool options: marketable_only (default true), min_velocity threshold, include_supply (default true).
|
|
35
55
|
- price_variant defaults to "best".
|
|
56
|
+
- Prefer Universalis for raw listings/sales history; use Saddlebag for competition metrics, rankings, and aggregate distributions.
|
|
57
|
+
- Saddlebag best-deals is excluded (premium gated + inconsistent API validation).
|
|
58
|
+
- Some Saddlebag endpoints call Universalis directly; be mindful of rate limits.
|
|
59
|
+
- saddlebag_get_raw_stats supports item_ids = -1 for all items and can return very large payloads.
|
|
60
|
+
- Wiki pages are curated snapshots; refresh with scripts/update-wiki-docs.mjs as needed.
|
|
61
|
+
- Set WIKI_FETCH_MODE=live to fetch allowlisted wiki pages from GitHub (cached by WIKI_REFRESH_TTL_MS).
|
|
36
62
|
`;
|
package/dist/services/clients.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import Bottleneck from "bottleneck";
|
|
2
|
-
import { DEFAULT_UNIVERSALIS_TIMEOUT_MS, DEFAULT_XIVAPI_TIMEOUT_MS } from "../constants.js";
|
|
2
|
+
import { DEFAULT_SADDLEBAG_TIMEOUT_MS, DEFAULT_UNIVERSALIS_TIMEOUT_MS, DEFAULT_XIVAPI_TIMEOUT_MS, } from "../constants.js";
|
|
3
|
+
import { SaddlebagClient } from "./saddlebag.js";
|
|
3
4
|
import { UniversalisClient } from "./universalis.js";
|
|
4
5
|
import { XivapiClient } from "./xivapi.js";
|
|
5
6
|
export function createClients(options = {}) {
|
|
@@ -15,7 +16,18 @@ export function createClients(options = {}) {
|
|
|
15
16
|
reservoirRefreshAmount: 10,
|
|
16
17
|
reservoirRefreshInterval: 1000,
|
|
17
18
|
});
|
|
19
|
+
const saddlebagLimiter = new Bottleneck({
|
|
20
|
+
maxConcurrent: 4,
|
|
21
|
+
reservoir: 20,
|
|
22
|
+
reservoirRefreshAmount: 10,
|
|
23
|
+
reservoirRefreshInterval: 1000,
|
|
24
|
+
});
|
|
18
25
|
return {
|
|
26
|
+
saddlebag: new SaddlebagClient({
|
|
27
|
+
limiter: saddlebagLimiter,
|
|
28
|
+
timeoutMs: options.saddlebagTimeoutMs ?? DEFAULT_SADDLEBAG_TIMEOUT_MS,
|
|
29
|
+
userAgent: options.userAgent,
|
|
30
|
+
}),
|
|
19
31
|
universalis: new UniversalisClient({
|
|
20
32
|
limiter: universalisLimiter,
|
|
21
33
|
timeoutMs: options.universalisTimeoutMs ?? DEFAULT_UNIVERSALIS_TIMEOUT_MS,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { SADDLEBAG_BASE_URL } from "../constants.js";
|
|
2
|
+
import { requestJson } from "./http.js";
|
|
3
|
+
export class SaddlebagClient {
|
|
4
|
+
baseUrl;
|
|
5
|
+
timeoutMs;
|
|
6
|
+
limiter;
|
|
7
|
+
userAgent;
|
|
8
|
+
constructor(options = {}) {
|
|
9
|
+
this.baseUrl = options.baseUrl ?? SADDLEBAG_BASE_URL;
|
|
10
|
+
this.timeoutMs = options.timeoutMs;
|
|
11
|
+
this.limiter = options.limiter;
|
|
12
|
+
this.userAgent = options.userAgent;
|
|
13
|
+
}
|
|
14
|
+
async post(path, body) {
|
|
15
|
+
return requestJson({
|
|
16
|
+
baseUrl: this.baseUrl,
|
|
17
|
+
path,
|
|
18
|
+
method: "POST",
|
|
19
|
+
body,
|
|
20
|
+
limiter: this.limiter,
|
|
21
|
+
timeoutMs: this.timeoutMs,
|
|
22
|
+
userAgent: this.userAgent,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|