skz46-official
Enriches discography detail results with release metadata pulled from the Sony Music release pages, including catalog numbers, tax-inclusive and pre-tax prices, catch copy, purchase privileges, shop links, and the per-disc track listings.
Click on "Deploy 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., "@skz46-officialshow me the latest Sakurazaka46 news"
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.
skz46-official (v0.4.1)
Agent Plugin (Agent Plugins 1.0.0 format) that wraps the public, no-login JSON endpoints of https://sakurazaka46.com in a zero-dependency stdio MCP server (Node ≥ 18, uses the built-in fetch).
Data shapes
type OfficialPost = {
kind: 'news' | 'blog';
id: string; // news: code (e.g. "R00638"); blog: article id ("71404")
title: string;
url: string; // absolute
published_at: string; // ISO 8601 in JST, e.g. "2026-10-15T21:00:00+09:00"
member_id?: string; // blog only
member_name?: string; // blog only (upstream "creator")
// news only (v0.4.0, upstream fields kept for hub use):
pubdate?: string; // official raw string, e.g. "2026/10/15 21:00:00" (JST), alongside ISO published_at
category?: string; // upstream code: release|goods|event|media|ticket|audition|shakehands|other
category_label?: string | null; // label as printed on /s/s46/news/list (リリース, グッズ, イベント, メディア, チケット, オーディション, ミート&グリート, その他, ファンクラブ)
caption?: string | null;
tags?: string[] | null; // upstream member-id tags (only some items have them; others are null)
content?: string | null; // article HTML; included by default, include_content:false omits it
thumbnail?: string | null; // absolute URL
};
type MeetGreetEvent = { code: string; date: string /* YYYY-MM-DD */; title: string;
category: string /* catename, e.g. オンライン */; category_code?: string /* catecode, e.g. online */;
url: string; image?: string };
type GreetingCard = { member_id: string; name: string; name_kana: string;
avatar?: string; card?: string; cheki?: string };v0.2.0 (scraped from server-rendered HTML; no JSON API exists for these):
type ScheduleEvent = { date: string /* YYYY-MM-DD */; start_time?: string /* "23:15"; may exceed 24:00 e.g. "25:56" (JP broadcast style) */;
end_time?: string; title: string; category: string /* media|event|release|shakehands|birthday (observed) */;
category_label?: string /* テレビ, ラジオ, … */; members?: { member_id?: string; name: string }[] /* group-wide entries have no member_id */;
description?: string /* plain text */; links?: string[]; url: string /* SCHEDULE page for that day */ };
type FormationMember = { member_id: string; name: string };
type Formation = { kind: 'single'|'album'; single_number?: number; album_number?: number; title: string; release_date: string;
discography_code?: string; discography_codes?: string[]; // resolved from the official discography list (type + release date + title), NOT the formation page jacket links
note?: string; // set when the page jacket links disagreed (e.g. 16th single links to 15th-single codes)
rows: { front?: FormationMember[]; second?: FormationMember[]; third?: FormationMember[] }; // = songs[0].rows (title track)
songs?: { title: string; song_titles?: string[] /* when one formation covers several songs */; rows: Formation['rows'] /* front|second|third|fourth */;
center?: { member_id?: string; name: string }; center_source?: 'official' | 'front_row_middle'; center_confidence?: 'high' | 'medium';
center_evidence?: { url: string; quote: string }; center_note?: string }[] };
// FormationMember.member_id is omitted for graduated members (no profile link).
type BlogPost = { id: string; title: string; member_id?: string; member_name?: string; published_at: string /* ISO +09:00 */;
url: string; body_text: string /* tags stripped, line breaks kept */; images?: string[] /* absolute */ };
type DiscographyItem = { code: string; title: string; type: string /* SINGLE|ALBUM|… */; release_date?: string; price_text?: string; jacket?: string; url: string };
type DiscographyDetail = { code: string; url: string; title: string; type?: string; nth?: string; release_date?: string;
catalog_price_text?: string; jacket?: string; other_editions?: DiscographyItem[];
sonymusic?: { source_url: string; title?: string; type?: string; release_date?: string; catalog_numbers?: string;
price?: number /* tax incl. */; price_extax?: number; catch_copy?: string; privileges?: string[]; shop_url?: string;
discs?: { disc_number: number; title?: string; tracks: { track_number: number; title: string }[] }[] };
sonymusic_error?: string };The list tools return { total, offset?, items }. total is the upstream count for news and blog, and items.length for the other two tools.
Normalization:
Relative paths are turned into
https://sakurazaka46.com/....The body is parsed as JSON even when the content-type says
text/html.pubdate(YYYY/MM/DD HH:MM:SS, JST) is converted to ISO with+09:00.Empty fields are omitted.
MeetGreetDetail (get_meet_greet_event)
{ code, title, url, category?, listed_date?, sessions?: MeetGreetSession[], members?: string[], body_text }
MeetGreetSession = { date?, section?, venue_label?, city?, venue?, part, start_time?, end_time?, reception_start?, reception_end?, raw_text }.
These are parsed from the page text: ●/■ XX会場 blocks, 開催日:/会場: lines, 【日程】 shared date lists (expanded per date), 第N部(HH:MM〜HH:MM) and <第N部> 受付開始 … / イベント開始 … / 受付終了 … (HH:MM 終了予定).
Full-width digits and colons are normalized, and times past 24:00 are kept as written. members comes from 【レーン詳細】/【参加メンバー】 blocks. Fields not on the page are omitted. get_schedule items include sessions when their description has 部 times.
Goods store (v0.4.0): StoreCategory, StoreProductSummary, StoreProduct
The source is the official goods store at store.plusmember.jp/sakurazaka46. It runs EC-CUBE 2, has no JSON API, and is read from server-rendered HTML.
StoreCategory = { id, name, level, url }.StoreProductSummary = { product_id, name, url, thumbnail?, price (tax-incl yen), price_text, tax_included, labels?, sold_out?, sub_text? }.Lists are 50 per page with
{ total, page, per_page, pages, category_name }.Page 1 is a GET. Later pages POST the list's own
pagenopaging form, because the store ignorespagenoin a GET. This is read-only.A page past the end returns
items: []with anote. The store would otherwise silently return page 1 again.
StoreProduct = { product_id, item_code, name, url, price, price_text, tax_included, item_price, categories[], labels?, comment, content, content_date, sales_start_text?, weight, image, images[], variation_labels?, variations?: [{classcategory_id1, classcategory_id2?, label, in_stock:true}], in_stock?, sold_out?, stock_flg, stock_source:'detail_page_signals', stock_note, description_text }.
Stock (what is public): these findings come from checking real products on 2026-10-16, including a guest-cart test (no login, nothing entered).
in_stockisfalsewhen the product shows the SOLD OUT label. Fully sold-out products also have no add-to-cart button.in_stockistruewhen the add-to-cart button is shown and there is no SOLD OUT label. Otherwise it is omitted.Sold-out variations are removed from the size/colour selector. Example: the stadium jumper 114894 lists sizes M/L/XL in its description, but the selector offers only XL. Adding size M to the cart is refused with 「…/M」は売り切れました. So
variations[]lists only purchasable variations, each within_stock: true. Sold-out variations are not listed and are never invented.stock_flgis the store's raw, undocumented flag. Observed values: 2 on in-stock items, and 0 on fully sold-out items and on 114894, which had only XL left. It is passed through raw and is not a count.Numeric stock counts are not public. A guest cart caps every line at the per-order sale limit (「一度に購入できる数量は50までです」), so a quantity probe shows "≥ 50" for normal stock. Since v0.4.1,
check_store_stockruns this probe on request (see below).
Image URLs from store tools go through the shared filter with the store hosts allowed: store.plusmember.jp, storage-store.plusmember.jp and image.emtg.jp. The store's user_data/.../img/common chrome is dropped, including the cover.gif lazy-load placeholder; the real thumbnail comes from the background-image.
Cart stock probe: check_store_stock (v0.4.1)
This tool creates a guest cart session on the live store. It is enabled by default. Set SKZ46_DISABLE_CART_PROBE=1 in the server's environment to disable it; calls then return a clear error and make no request.
Each call does the following:
Uses a fresh, isolated cookie jar (a new guest session) and opens the product page.
If the product shows SOLD OUT or has no add-to-cart button, it answers
available: falsewithout touching the cart.Otherwise it adds 50 (the store's per-order cap) of the product or variation and reads the cart line and messages.
In a
finallyblock it always deletes every cart line and re-reads the cart to verify it is empty (cart_emptied).It never goes to checkout, never logs in and never sends personal data. A call is about 5–6 requests, each at least 1 s apart.
{ product_id, name, classcategory_id1, classcategory_id2, requested_quantity: 50, probe: 'guest_cart', available, at_least, exact, exact_basis, capped_by_order_limit, order_limit, variation_listed_on_page, message_raw, cart_line, cart_emptied, cart_touched, cleanup_error, reservation_note }
at_least: how many the cart accepted. 50 means "at least 50";capped_by_order_limitis then true andexactisnull.exact:It is set to 0 when the store says 売り切れました, e.g. 114894 size M.
It is set to the accepted quantity when the cart cut the requested 50 to fewer. That behaviour was not observed: every in-stock variation probed on 2026-10-16 accepted 50. In EC-CUBE 2, the cart lowers an over-limit quantity to the smaller of stock and the sale limit, so a smaller number would mean stock.
Reservation: adding to cart does not reserve stock (high confidence).
Every product page says 「ご注文確定するまでは、カートに商品が入っていても在庫は確保されていません。ご注文手続き中に在庫切れとなる場合があります。」 (until the order is confirmed, items in the cart are not secured).
In EC-CUBE 2 the stock is decremented only when an order is placed, not when an item is added to the cart.
The probe also empties its cart immediately.
SpecialPageRef / SpecialPage (v0.3.0)
SpecialPageRef = { slug, title?, date?, url, source: 'live'|'archive', thumbnail? } comes from list_special_pages. Items are de-duplicated by slug; when a page is on both lists, the live entry wins.
SpecialPage = { slug, url, title?, text, truncated?, text_length?, images?: string[], links?: {url, text?, external?}[], videos?: {provider:'youtube'|'brightcove', id, url?, account?}[] }.
text is plain text from the page <body>. Headings become #/## lines; scripts, styles, nav, the site header, footer, cookie banner and share widgets are removed. It is capped at 20,000 characters, with truncated: true when cut. No page-specific fields are parsed, because the pages are hand-built landing pages.
Image filter (all tools, v0.3.0)
server/images.js handles every image the tools return (images[], thumbnail, jacket, avatar/card/cheki, image):
Makes URLs absolute (https) and removes
?ima=0000.Prefers the largest
data-srcset/srcset, thendata-src/data-original/data-lazy-src, over a placeholdersrc.Drops
data:URIs, 1×1 ordisplay:nonepixels, and third-party hosts. Onlysakurazaka46.comand*.sonymusic.co.jpare kept, so the Facebook pixel, OneTrust logos and YouTube thumbnails go.Drops site-chrome paths
/files/14/s46/img/,/files/14/s46/images/,/files/14/common/and/files/14/app/.Drops file names matching icon/arrow/logo/sprite/spacer/blank/loading/placeholder/noimage/btn/share/sns/emoji, and
.svg/.ico.Removes duplicates.
Content images are kept:
/images/14/...,/files/14/diary/...blog photos and per-page/files/14/sakura/pages/<slug>/images/...art.
Related MCP server: Shopify Storefront MCP Server
Members, aliases and fan terms (v0.4.0)
server/members.jsonwas scraped once on 2026-10-16 from/s/s46/search/artist?display=generationand each/s/s46/artist/<id>profile (name,kana,eigoromaji). No network calls are made at runtime.aliasesholds only mechanically derived zh-Hant/zh-Hans spellings, each with asource. No curated nicknames are included because none could be sourced from official pages. To add one, use{alias, lang, source}with a public source.list_blog_posts,list_greeting_cardsandget_scheduleacceptmember(a name or alias) as well asmember_id. Ambiguous names return an error that lists the candidates.get_schedulehas these filters:label: the exact site sub-type, e.g. テレビ, ラジオ, 雑誌, 配信, WEBメディア, 新聞, ライブ.memberandkeyword.genre: "music_tv": a heuristic music-program title list. Items carrygenre_match: "heuristic".
The
memberfilter keeps only items that list that member, so group-wide items (member shown as 櫻坂46) are excluded.SKILL.md has a zh/ja fan-term glossary that maps terms to tools and params.
Hub-compat conventions (v0.4.0)
Explicit nulls: when a field is empty at the source or could not be parsed, it is returned as
nullinstead of being left out. This applies to every tool. Empty strings becomenull. Optional lists arenullwhen empty, while result containers (items) stay[].Exceptions:
contentis left out wheninclude_content: false, andrawonly appears when requested.
raw: true(every tool): the result also hasraw: [{ url, method, http_status, content_type, fetched_at, sha256, body }], with one entry per upstream request the call made, in order.fetched_atis ISO 8601 UTC.sha256is the hex digest of the UTF-8 body text exactly as received.bodyis that raw text.Requests answered from the per-process discography cache (used by
get_formationto resolve codes) make no upstream request, so they don't appear.
Opt-in behaviour:
get_formationcenter inference needsinfer_center: true. Sony Music calls needinclude_sonymusic: true.
ask: one-call entry point (v0.4.1)
ask {query, date_range?} is listed first and is the recommended entry point. It parses the question with fixed rules, with no model and no guessing: fan terms (音番, 見面會/ミーグリ 第N部 + city, ブログ/部落格, N單 / 第N張單曲 / Nth シングル, C位/センター, 站位/選拔, 周邊/グッズ 有貨嗎/在庫, 行程/スケジュール, 電視/ラジオ/雜誌, 新聞), member names and nicknames (incl. kanji variants 島/嶋, 崎/﨑 and zh spellings), and JST dates (今天, 明天, 這週, 下週, 下個月, N月, M/D). It then calls the matching tool or tools and returns {interpreted_as:{intent, params, members, dates, notes}, confidence, ambiguous, result, next}. If the intent or member is ambiguous (e.g. "遠藤 blog"), it returns candidates and calls nothing.
Meet&greet with a city or 第N部: reads at most 3 matching event pages and returns only the matching sessions.
Stock questions: searches the store. If exactly one product matches, it returns
get_store_product(public in_stock) and suggestscheck_store_stockinnext. It never runs the cart probe by itself.Center: calls
get_formationwithinfer_center: trueand returns a per-songcenters[]with source/confidence (official evidence = high, front-row middle = medium).Upcoming first (JST). Meet&greet, schedule and live/concert questions only look at future events, unless the query clearly asks about the past: a single number (N單/第N枚), 上次/之前/去年/過去/已經, or an explicit past date. A schedule question with no date covers the next 30 days.
Meet&greet with no upcoming match: returns
none_upcoming: true, an emptymatches, andlatest_past_event+next_hintas a pointer only, not as the answer.Live/concert questions (e.g. "大阪演唱會", intent
live): checks schedule items labeled ライブ for the next 3 months plus LIVE special pages, matching the city in kanji or romaji (OSAKA). If nothing upcoming matches, it returns past matches from the last 6 months, each markedis_past: true, with a "no upcoming" note. It also lists upcoming live pages that name no city inother_upcoming_live_without_city_match.Solo vs festival (heuristic,
live_type_source: 'heuristic'): each live item gets alive_type.festivalmeans a multi-artist event (フェス/FESTIVAL, GIGANTIC, ROCK IN JAPAN, COUNTDOWN JAPAN/CDJ, TIF, アラバキ, SUMMER SONIC, JAPAN JAM, 音楽祭, …).solomeans Sakurazaka46's own lives (櫻坂46「…」, BACKS LIVE, ANNIVERSARY LIVE, ARENA TOUR/ツアー, 期生LIVE, …). Anything else isunknown.演唱會/公演/ライブ/ツアー queries are answered with solo (and unknown) items only. 音樂節/フェス/festival names are answered with festivals only.
If a concert query finds no solo match, festivals may be attached as
festival_hint, labeled hint-only and not the answer. The upcoming-first rule applies to both.
Venues:
server/venues.jsonmaps about 40 major venues (Osaka, Tokyo, Kanagawa, Saitama, Chiba, Aichi, Fukuoka, Hokkaido, Miyagi, Hiroshima, Hyogo, Kyoto, Kagawa) and their aliases to city/prefecture. The venue is read from schedule 【会場】 lines and from live pages (### 会場 / 会場・日程; up to 4 upcoming pages are read). Items carryvenue,venue_raw,cityandprefecture. A query matches on city OR venue, and venue names work directly (e.g. "京セラ ライブ").Past fallback covers all history (schedule back to 2020-10, LIVE + ARCHIVE pages; the meet&greet list API is already complete). Results are newest first, capped (3 shows for live, 10 items for schedule), and labeled
is_past: truewith a "no upcoming" note. Multi-day shows are merged intodates[]. Tour legs are matched to the date via 《県》venue blocks. Cost: about 1 s per month walked back; once at least one match is found it stops after about 50 s.
Every result also has next: suggested follow-up calls {tool, params, why} (e.g. list → detail), or null.
Offline eval: node test/eval.mjs (54 fan queries + 10 live_type cases in test/eval-queries.json).
Tools
Tool | Args | Upstream |
|
|
|
|
|
|
| — |
|
| — | store.plusmember.jp/sakurazaka46 menu (HTML) |
|
|
|
|
|
|
|
| live guest-cart probe on store.plusmember.jp (enabled by default; set |
|
| static table |
| — | LIVE list + all ARCHIVE list pages (HTML) → |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| — |
|
|
|
|
|
|
|
Notes on the v0.2.0 tools:
Schedule:
dy=YYYYMMreturns that month.dy=YYYYMMDDalso returns the whole month, so filtering bydateandcategoryhappens in the plugin.Third-party source (opt-in): track lists, prices and bonuses in
get_discography.sonymusiccome from Sony Music Japan's JSON (the same request the official page makes). Since v0.4.0 the call is made only wheninclude_sonymusic: true; otherwisesonymusicisnull. If the call fails, the official part is still returned along withsonymusic_error.Formation center: the page has no explicit center marker.
Since v0.4.0, position-based inference is off by default. Without official evidence,
center,center_sourceandcenter_confidencearenull.Pass
infer_center: trueto get the middle member of an odd-sized front row, withcenter_source: 'front_row_middle'andcenter_confidence: 'medium'. An even or empty front row still givesnull.Official evidence (below) is always returned, with
center_source: 'official'.It becomes
center_source: 'official'withcenter_confidence: 'high'only when the static fileserver/center-evidence.jsoncites official evidence (a news item or special/TV page URL plus a quote). That file was hand-verified on 2026-10-16: the title songs of singles 1–11 and 13–16, 15's 2nd A-side, and 11 coupling songs.If official evidence names someone other than the middle member, the official name wins and
center_noteexplains the difference.No news is crawled at runtime. New singles have no center (or medium with
infer_center) until someone adds evidence to the file.get_formationalso takesalbum: N.
Errors: if the expected HTML anchors are missing, the tools return
page structure changed: … not found at <url>.
Upstream behaviour:
News and blog return at most 200 items per request.
offsetmaps to the upstreamst(0-based).Calls to upstream are spaced at least 1 s apart.
Errors come back as MCP
isErrorresults: non-200 status, non-JSON body, or{"result":"NG"}.
Install
The MCP server ships as the npm package skz46-official-mcp. It has no dependencies and needs Node ≥ 18. Every client starts it the same way:
npx -y skz46-official-mcpAs an Agent Plugin (Cursor or any Agent Plugins host): use this directory as-is. plugin.json plus mcp.json declare the server (npx -y skz46-official-mcp, with no path variables) and the skill in skills/. For a local test in Cursor, copy or symlink the directory to ~/.cursor/plugins/local/skz46-official.
Cursor (MCP only): add this to ~/.cursor/mcp.json or .cursor/mcp.json:
{ "mcpServers": { "skz46-official": { "command": "npx", "args": ["-y", "skz46-official-mcp"] } } }Claude Code: claude mcp add skz46-official -- npx -y skz46-official-mcp
Claude Desktop: add the same mcpServers entry as for Cursor to claude_desktop_config.json.
Other MCP clients (stdio): command npx, args ["-y", "skz46-official-mcp"]. Or install it globally with npm i -g skz46-official-mcp and use the command skz46-official-mcp.
Before it's published to npm: run npm pack and point npx at the tarball: npx -y --package=/abs/path/skz46-official-mcp-0.1.0.tgz skz46-official-mcp (a bare npx -y <tarball> fails with "Permission denied").
Layout
plugin.json Agent Plugins manifest
mcp.json stdio server definition (npx -y skz46-official-mcp)
package.json npm package (bin: skz46-official-mcp -> server/index.js)
LICENSE MIT
server/index.js MCP server (JSON-RPC 2.0: initialize, tools/list, tools/call, ping) + JSON tools
server/html-tools.js HTML-scraping tools (schedule, formation, blog post, discography)
server/center-evidence.json hand-verified official center citations
skills/sakurazaka46-official/SKILL.md
test/smoke.mjs end-to-end test against the live siteTest
node test/smoke.mjs [command args...] spawns the server (by default node server/index.js), runs initialize, ping and tools/list, then calls every tool against the live site. Examples:
node test/smoke.mjs npx -y --package=$PWD/skz46-official-mcp-0.1.0.tgz skz46-official-mcp or node test/smoke.mjs skz46-official-mcp.
Publishing (owner)
npm login
npm publish --access publicThe package contents are limited by the files field to server/, README.md and LICENSE. package.json is always included.
Disclaimer
English. This is an unofficial, fan-made project. It is not affiliated with, endorsed by, or sponsored by Sakurazaka46, Seed & Flower LLC, or Sony Music. All content, names, trademarks and images belong to their respective rights holders. The server fetches publicly available pages from the official site on demand and returns extracted text (including blog post and special page body text), links and image URLs; it does not download, store or rehost images. Please respect the official site's terms of use and view the originals on the official site. Rights holders may request changes or removal by opening a GitHub issue.
日本語. 本プロジェクトはファンによる非公式のものであり、櫻坂46、Seed & Flower合同会社、ソニーミュージックとは一切関係がなく、承認・後援を受けたものではありません。すべてのコンテンツ、名称、商標、画像の権利は各権利者に帰属します。本サーバーは公式サイトで一般公開されているページを必要に応じて取得し、抽出したテキスト(ブログ記事や特設ページの本文を含む)、リンク、画像URLを返します。画像のダウンロード・保存・再配布は行いません。公式サイトの利用規約を尊重し、原文・原画像は公式サイトでご覧ください。権利者の方は GitHub の Issue から修正・削除をご依頼いただけます。
This server cannot be deployed
Maintenance
Related MCP Connectors
Public web tools for agents: product extraction, claim checks, webpage QA and ranked audits.
Public K-beauty catalog, product search, and latest-offers tools with canonical product facts.
AI-powered commerce API for luxury skincare shopping. Enables AI agents to search products, browse collections, manage shopping carts, and generate checkout URLs for the Regenique Elegance Shopify store.
The CWI Discovery Engine is the MCP server for the Cumulative Web Inc music catalog — 28 tools for AI agents: catalog search, track/artist/release lookups, semantic and mood-based sync search, Radio 365 live stream data, playlist pitching, sync-brief submission, airplay requests, featured nominations, label info, events, lyrics, press kits, videos, genres, playlists and recommendations. No authentication required. Free.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceProvides comprehensive access to Blue Archive game data including students, equipment, stages, and multimedia content via the SchaleDB API. It enables AI assistants to perform advanced searches and retrieve detailed game information in English, Japanese, and Chinese.14 npmMIT
- FlicenseNot gradedqualityDmaintenanceEnables AI assistants to query and interact with Shopify store data via the Storefront API, including products, collections, carts, and customer information.9-
- AlicenseAqualityAmaintenanceEnables LLMs to search and retrieve Japanese laws from the e-Gov API v2, including keyword search, article fetching, table of contents, and revision history.71,479 npm2MIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to query Japanese public data (laws, corporations, statistics) from official government APIs, returning normalized English metadata with source attribution.1MIT