@akiojin/playfab-mcp-server
Version:
PlayFab MCP Server
1,320 lines (1,319 loc) • 48 kB
JavaScript
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
import * as dotenv from "dotenv";
import * as pf from "playfab-sdk";
const PlayFab = pf.PlayFab;
const PlayFabAdminAPI = pf.PlayFabAdmin;
const PlayFabAuthenticationAPI = pf.PlayFabAuthentication;
const PlayFabEconomyAPI = pf.PlayFabEconomy;
const PlayFabProfileAPI = pf.PlayFabProfiles;
dotenv.config();
PlayFab.settings.titleId = process.env.PLAYFAB_TITLE_ID;
PlayFab.settings.developerSecretKey = process.env.PLAYFAB_DEV_SECRET_KEY;
const SEARCH_ITEMS_TOOL = {
name: "search_items",
description: "Searches for items in the PlayFab catalog (Economy v2). Use this when you need to find items by name, type, or other properties. " +
"Common uses: Finding all weapons, searching for items containing 'sword', filtering by price range. " +
"Returns item details including ID, name, description, and prices. " +
"Supports pagination for large result sets. Use the returned items' IDs with inventory management tools.",
inputSchema: {
type: "object",
properties: {
Count: {
type: "number",
description: "Number of items to retrieve per page. Maximum is 50. Default is 10."
},
ContinuationToken: {
type: "string",
description: "Token for retrieving the next page of results. Use null or omit for the first request."
},
Filter: {
type: "string",
description: "OData filter string to refine the search. Example: 'type eq \'ugc\''"
},
OrderBy: {
type: "string",
description: "OData orderBy string to sort results. Example: 'rating/average asc'"
},
Search: {
type: "string",
description: "Text to search for in the catalog. Example: 'sword'"
}
},
required: [
"Count"
],
},
};
const GET_ALL_SEGMENTS_TOOL = {
name: "get_all_segments",
description: "Retrieves an array of player segment definitions. " +
"Results from this can be used in subsequent API calls " +
"such as GetPlayersInSegment which requires a Segment ID. " +
"While segment names can change the ID for that segment will not change.",
inputSchema: {
type: "object",
properties: {},
required: [],
},
};
const GET_PLAYERS_IN_SEGMENTS_TOOL = {
name: "get_players_in_segments",
description: "Retrieves a paginated list of player profiles in a specific segment." +
"Use SegmentId to specify the segment. Use ContinuationToken for pagination.",
inputSchema: {
type: "object",
properties: {
SegmentId: {
type: "string",
description: "The ID of the segment to retrieve players from. You can get this from the get_all_segments tool."
},
ContinuationToken: {
type: "string",
description: "Token for retrieving the next page of results. Use null or omit for the first request."
},
},
required: [
"SegmentId"
],
},
};
const ADD_INVENTORY_ITEMS_TOOL = {
name: "add_inventory_items",
description: "Grants items or virtual currency to a player's inventory. Use this when you need to: " +
"1) Give rewards to players, 2) Add purchased items, 3) Grant virtual currency (use currency item ID). " +
"For bulk operations across multiple players, use grant_items_to_users instead. " +
"Note: In Economy v2, virtual currencies are items - use their item IDs, not currency codes.",
inputSchema: {
type: "object",
properties: {
TitlePlayerAccountId: {
type: "string",
description: "The unique Title Player Account ID of the player to whom the item will be added. Example: 'E8F301D78C4C2346'"
},
Amount: {
type: "number",
description: "How many of the item to add. Must be a positive integer. Example: 1"
},
CollectionId: {
type: "string",
description: "The collection to add the item to. Use 'default' unless you have a custom collection."
},
DurationInSeconds: {
type: "number",
description: "How long (in seconds) until the item expires. Omit for permanent items."
},
IdempotencyId: {
type: "string",
description: "A unique string to prevent duplicate requests. Use a UUID."
},
Item: {
type: "object",
description: "The item to add, as an InventoryItemReference object. At minimum, specify the Id (item ID)." +
"StackId defaults to 'default', grouping items into the same stack; to create a separate stack for the same item ID, provide a unique StackId (e.g. a UUID)." +
"AlternateId is for alternative item identifiers (e.g. external keys)." +
"Example: { 'Id': 'item_12345' } or { 'Id': 'item_12345', 'StackId': 'unique_stack_001' }." +
"See: https://learn.microsoft.com/ja-jp/rest/api/playfab/economy/inventory/add-inventory-items?view=playfab-rest#inventoryitemreference"
},
NewStackValues: {
type: "object",
description: "Values to apply to a new stack created by this request. Use for custom display properties, etc. Optional."
}
},
required: [
"TitlePlayerAccountId",
"Item"
],
},
};
const GET_INVENTORY_ITEMS_TOOL = {
name: "get_inventory_items",
description: "Retrieves the current inventory items for a specific player." +
"You must provide the TitlePlayerAccountId." +
"You can optionally specify a collection (CollectionId), a page size (Count), and a ContinuationToken for pagination.",
inputSchema: {
type: "object",
properties: {
Count: {
type: "number",
description: "Number of items to retrieve per page. Maximum is 50. Default is 10. Example: 10"
},
CollectionId: {
type: "string",
description: "The collection ID to retrieve items from. Use 'default' unless you have a custom collection. Example: 'default'"
},
ContinuationToken: {
type: "string",
description: "Token for retrieving the next page of results. Use null or omit for the first request."
},
TitlePlayerAccountId: {
type: "string",
description: "The unique Title Player Account ID of the player whose inventory you want to retrieve. Example: 'E8F301D78C4C2346'"
}
},
required: [
"Count",
"TitlePlayerAccountId",
],
},
};
const GET_INVENTORY_COLLECTION_IDS_TOOL = {
name: "get_inventory_collection_ids",
description: "Retrieves all inventory collection IDs for a specific player." +
"Use this to list all collections before fetching items from them.",
inputSchema: {
type: "object",
properties: {
Count: {
type: "number",
description: "Number of collection IDs to retrieve per page. Maximum is 50. Default is 10."
},
ContinuationToken: {
type: "string",
description: "Token for retrieving the next page of results. Use null or omit for the first request."
},
TitlePlayerAccountId: {
type: "string",
description: "The unique Title Player Account ID of the player."
}
},
required: [
"Count",
"TitlePlayerAccountId",
],
},
};
const GET_TITLE_PLAYER_ACCOUNT_ID_FROM_PLAYFAB_ID_TOOL = {
name: "get_title_player_account_id_from_playfab_id",
description: "Converts a PlayFabId to TitlePlayerAccountId. IMPORTANT: Use this before any inventory operations! " +
"PlayFabId (from login/user info) ≠ TitlePlayerAccountId (needed for inventory). " +
"Example flow: Get PlayFabId from user data → Convert with this tool → Use result for inventory APIs.",
inputSchema: {
type: "object",
properties: {
PlayFabId: {
type: "string",
description: "The PlayFabId (master player account ID) to convert. Example: '276427AE27FF98AC'"
}
},
required: ["PlayFabId"],
},
};
const DELETE_INVENTORY_ITEMS_TOOL = {
name: "delete_inventory_items",
description: "Deletes items from a player's inventory. " +
"You must specify the Item (InventoryItemReference object) and TitlePlayerAccountId. " +
"Warning: This permanently removes items from the player's inventory.",
inputSchema: {
type: "object",
properties: {
TitlePlayerAccountId: {
type: "string",
description: "The unique Title Player Account ID of the player whose items will be deleted."
},
CollectionId: {
type: "string",
description: "The collection to delete items from. Use 'default' unless you have a custom collection."
},
Item: {
type: "object",
description: "The item to delete, as an InventoryItemReference object. Specify the Id and optionally StackId."
},
IdempotencyId: {
type: "string",
description: "A unique string to prevent duplicate requests. Use a UUID."
}
},
required: [
"TitlePlayerAccountId",
"Item"
],
},
};
const SUBTRACT_INVENTORY_ITEMS_TOOL = {
name: "subtract_inventory_items",
description: "Subtracts a specific amount of items from a player's inventory. " +
"Use this to reduce item quantities without completely removing them.",
inputSchema: {
type: "object",
properties: {
TitlePlayerAccountId: {
type: "string",
description: "The unique Title Player Account ID of the player."
},
Amount: {
type: "number",
description: "How many of the item to subtract. Must be a positive integer."
},
CollectionId: {
type: "string",
description: "The collection to subtract items from. Use 'default' unless you have a custom collection."
},
Item: {
type: "object",
description: "The item to subtract, as an InventoryItemReference object."
},
IdempotencyId: {
type: "string",
description: "A unique string to prevent duplicate requests. Use a UUID."
},
DurationInSeconds: {
type: "number",
description: "How long (in seconds) until the subtraction expires. Omit for permanent subtraction."
}
},
required: [
"TitlePlayerAccountId",
"Amount",
"Item"
],
},
};
const UPDATE_INVENTORY_ITEMS_TOOL = {
name: "update_inventory_items",
description: "Updates properties of existing inventory items. " +
"Use this to modify item metadata, display properties, or custom data.",
inputSchema: {
type: "object",
properties: {
TitlePlayerAccountId: {
type: "string",
description: "The unique Title Player Account ID of the player."
},
CollectionId: {
type: "string",
description: "The collection containing the items. Use 'default' unless you have a custom collection."
},
Item: {
type: "object",
description: "The item to update, as an InventoryItemReference object with Id and optionally StackId."
},
IdempotencyId: {
type: "string",
description: "A unique string to prevent duplicate requests. Use a UUID."
}
},
required: [
"TitlePlayerAccountId",
"Item"
],
},
};
const EXECUTE_INVENTORY_OPERATIONS_TOOL = {
name: "execute_inventory_operations",
description: "Execute multiple inventory operations in a single batch request. " +
"Supports up to 50 operations per request. Operations are atomic - all succeed or all fail.",
inputSchema: {
type: "object",
properties: {
Operations: {
type: "array",
description: "Array of operations to execute.",
items: {
type: "object",
properties: {
Add: {
type: "object",
description: "Add operation details",
properties: {
Item: { type: "object" },
Amount: { type: "number" },
DurationInSeconds: { type: "number" }
}
},
Delete: {
type: "object",
description: "Delete operation details",
properties: {
Item: { type: "object" }
}
},
Subtract: {
type: "object",
description: "Subtract operation details",
properties: {
Item: { type: "object" },
Amount: { type: "number" }
}
},
Update: {
type: "object",
description: "Update operation details",
properties: {
Item: { type: "object" }
}
}
}
}
},
Entity: {
type: "object",
description: "Target entity for operations",
properties: {
Id: { type: "string", description: "Title Player Account ID" },
Type: { type: "string", enum: ["title_player_account"] }
},
required: ["Id", "Type"]
},
CollectionId: {
type: "string",
description: "Collection ID (default: 'default')"
},
IdempotencyId: {
type: "string",
description: "Unique ID to prevent duplicate operations"
}
},
required: ["Operations", "Entity"],
},
};
const BAN_USERS_TOOL = {
name: "ban_users",
description: "Bans players from accessing the game. Use when: 1) Player violates terms, 2) Suspicious activity detected, 3) Temporary suspension needed. " +
"Can ban by: PlayFabId (specific player), IPAddress (block IP), MACAddress (block device). " +
"Set DurationInHours for temp bans, omit for permanent. Always include clear Reason for records.",
inputSchema: {
type: "object",
properties: {
Bans: {
type: "array",
description: "Array of ban requests.",
items: {
type: "object",
properties: {
PlayFabId: { type: "string", description: "PlayFab ID to ban" },
IPAddress: { type: "string", description: "IP address to ban" },
MACAddress: { type: "string", description: "MAC address to ban" },
Reason: { type: "string", description: "Reason for the ban" },
DurationInHours: { type: "number", description: "Ban duration in hours (optional, permanent if not specified)" }
}
}
}
},
required: ["Bans"],
},
};
const REVOKE_ALL_BANS_FOR_USER_TOOL = {
name: "revoke_all_bans_for_user",
description: "Removes all active bans for a specific player. " +
"This unbans the player completely.",
inputSchema: {
type: "object",
properties: {
PlayFabId: {
type: "string",
description: "The PlayFab ID of the player to unban."
}
},
required: ["PlayFabId"],
},
};
const GET_USER_ACCOUNT_INFO_TOOL = {
name: "get_user_account_info",
description: "Retrieves detailed account information for a player. " +
"Includes profile data, statistics, and linked accounts.",
inputSchema: {
type: "object",
properties: {
PlayFabId: {
type: "string",
description: "The PlayFab ID of the player."
}
},
required: ["PlayFabId"],
},
};
const GET_USER_DATA_TOOL = {
name: "get_user_data",
description: "Retrieves custom data stored for a player. " +
"Can retrieve specific keys or all data.",
inputSchema: {
type: "object",
properties: {
PlayFabId: {
type: "string",
description: "The PlayFab ID of the player."
},
Keys: {
type: "array",
items: { type: "string" },
description: "Specific data keys to retrieve (optional, retrieves all if not specified)."
}
},
required: ["PlayFabId"],
},
};
const UPDATE_USER_DATA_TOOL = {
name: "update_user_data",
description: "Updates custom data for a player. " +
"Can set multiple key-value pairs at once.",
inputSchema: {
type: "object",
properties: {
PlayFabId: {
type: "string",
description: "The PlayFab ID of the player."
},
Data: {
type: "object",
description: "Key-value pairs of data to update.",
additionalProperties: { type: "string" }
},
Permission: {
type: "string",
enum: ["Private", "Public"],
description: "Data permission level. Default is 'Private'."
}
},
required: ["PlayFabId", "Data"],
},
};
const SET_TITLE_DATA_TOOL = {
name: "set_title_data",
description: "Sets global game configuration visible to ALL players. Use for: " +
"1) Game version info, 2) Event schedules, 3) Feature flags, 4) Global settings. " +
"Format: Key-value pairs. Value can be JSON string for complex data. " +
"WARNING: This is PUBLIC data - use set_title_internal_data for sensitive configs!",
inputSchema: {
type: "object",
properties: {
Key: {
type: "string",
description: "The key for the title data."
},
Value: {
type: "string",
description: "The value to set (JSON string for complex data)."
}
},
required: ["Key", "Value"],
},
};
const GET_TITLE_DATA_TOOL = {
name: "get_title_data",
description: "Retrieves global configuration data for the title. " +
"Can retrieve specific keys or all title data.",
inputSchema: {
type: "object",
properties: {
Keys: {
type: "array",
items: { type: "string" },
description: "Specific keys to retrieve (optional, retrieves all if not specified)."
}
},
},
};
const SET_TITLE_INTERNAL_DATA_TOOL = {
name: "set_title_internal_data",
description: "Sets server-only title data that is not accessible by clients. " +
"Use this for sensitive configuration like API keys, server settings, etc.",
inputSchema: {
type: "object",
properties: {
Key: {
type: "string",
description: "The key for the internal data."
},
Value: {
type: "string",
description: "The value to set (JSON string for complex data)."
}
},
required: ["Key", "Value"],
},
};
const GET_TITLE_INTERNAL_DATA_TOOL = {
name: "get_title_internal_data",
description: "Retrieves server-only title data. " +
"This data is not accessible by game clients.",
inputSchema: {
type: "object",
properties: {
Keys: {
type: "array",
items: { type: "string" },
description: "Specific keys to retrieve (optional, retrieves all if not specified)."
}
},
},
};
const CREATE_DRAFT_ITEM_TOOL = {
name: "create_draft_item",
description: "Creates a new draft item in the catalog. Draft items must be published before they can be used. " +
"Use this to: 1) Add new items to your game, 2) Create virtual currency items, 3) Define bundles. " +
"After creation, use publish_draft_item to make it available to players.",
inputSchema: {
type: "object",
properties: {
Item: {
type: "object",
description: "The item definition",
properties: {
Type: {
type: "string",
description: "Item type (e.g., 'catalogItem', 'currency', 'bundle')"
},
Title: {
type: "object",
description: "Localized titles. Example: { 'en': 'Sword of Fire', 'ja': '炎の剣' }",
additionalProperties: { type: "string" }
},
Description: {
type: "object",
description: "Localized descriptions",
additionalProperties: { type: "string" }
},
StartDate: {
type: "string",
description: "When the item becomes available (ISO 8601 format)"
},
EndDate: {
type: "string",
description: "When the item expires (ISO 8601 format)"
},
IsHidden: {
type: "boolean",
description: "Whether the item is hidden from players"
},
IsStackable: {
type: "boolean",
description: "Whether multiple can be stacked in inventory"
},
DisplayProperties: {
type: "object",
description: "Custom display properties",
additionalProperties: true
},
PriceOptions: {
type: "object",
description: "Pricing configuration",
properties: {
Prices: {
type: "array",
items: {
type: "object",
properties: {
Amounts: {
type: "array",
items: {
type: "object",
properties: {
ItemId: { type: "string" },
Amount: { type: "number" }
}
}
}
}
}
}
}
}
},
required: ["Type"]
},
Publish: {
type: "boolean",
description: "Whether to publish immediately after creation"
}
},
required: ["Item"],
},
};
const UPDATE_DRAFT_ITEM_TOOL = {
name: "update_draft_item",
description: "Updates an existing draft item in the catalog. " +
"Changes only affect the draft version until published.",
inputSchema: {
type: "object",
properties: {
ItemId: {
type: "string",
description: "The ID of the item to update"
},
Item: {
type: "object",
description: "Updated item properties (same structure as create_draft_item)"
},
Publish: {
type: "boolean",
description: "Whether to publish immediately after update"
}
},
required: ["ItemId", "Item"],
},
};
const DELETE_ITEM_TOOL = {
name: "delete_item",
description: "Permanently deletes an item from the catalog. " +
"WARNING: This cannot be undone! The item will be removed from all player inventories.",
inputSchema: {
type: "object",
properties: {
ItemId: {
type: "string",
description: "The ID of the item to delete"
}
},
required: ["ItemId"],
},
};
const PUBLISH_DRAFT_ITEM_TOOL = {
name: "publish_draft_item",
description: "Publishes a draft item, making it available to players. " +
"Once published, the item can be purchased and used in the game.",
inputSchema: {
type: "object",
properties: {
ItemId: {
type: "string",
description: "The ID of the draft item to publish"
},
ETag: {
type: "string",
description: "Optional ETag for concurrency control"
}
},
required: ["ItemId"],
},
};
const GET_ITEM_TOOL = {
name: "get_item",
description: "Retrieves detailed information about a specific catalog item. " +
"Returns both draft and published versions if available.",
inputSchema: {
type: "object",
properties: {
ItemId: {
type: "string",
description: "The ID of the item to retrieve"
}
},
required: ["ItemId"],
},
};
async function SearchItems(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.SearchItems(params, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
}
else {
resolve({
success: true,
items: result.data.Items,
continuationToken: result.data.ContinuationToken
});
}
});
});
}
async function GetAllSegments(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.GetAllSegments({}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
}
else {
resolve({
success: true,
segments: result.data.Segments || []
});
}
});
});
}
async function GetPlayersInSegments(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.GetPlayersInSegment(params, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
players: result.data.PlayerProfiles,
continuationToken: result.data.ContinuationToken,
profilesInSegment: result.data.ProfilesInSegment
});
});
});
}
async function GetInventoryItems(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.GetInventoryItems({
Count: params.Count,
CollectionId: params.CollectionId,
ContinuationToken: params.ContinuationToken,
Entity: {
Id: params.TitlePlayerAccountId,
Type: "title_player_account"
},
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
inventory: result.data.Items,
continuationToken: result.data.ContinuationToken,
});
});
});
}
async function GetInventoryCollectionIds(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.GetInventoryCollectionIds(params, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
collectionIds: result.data.CollectionIds,
continuationToken: result.data.ContinuationToken,
});
});
});
}
async function GetTitlePlayerAccountIdFromPlayFabId(params) {
return new Promise((resolve, reject) => {
PlayFabProfileAPI.GetTitlePlayersFromMasterPlayerAccountIds({
TitleId: PlayFab.settings.titleId,
MasterPlayerAccountIds: [params.PlayFabId],
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
const accounts = result.data.TitlePlayerAccounts || {};
const account = accounts[params.PlayFabId];
if (account && account.Id) {
resolve({
success: true,
titlePlayerAccountId: account.Id,
});
}
else {
reject("TitlePlayerAccountId not found");
}
});
});
}
async function AddInventoryItems(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.AddInventoryItems({
Amount: params.Amount,
CollectionId: params.CollectionId,
DurationInSeconds: params.DurationInSeconds,
IdempotencyId: params.IdempotencyId,
Item: params.Item,
NewStackValues: params.NewStackValues,
Entity: {
Id: params.TitlePlayerAccountId,
Type: "title_player_account"
},
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
eTag: result.data.ETag,
idempotencyId: result.data.IdempotencyId,
transactionIds: result.data.TransactionIds,
});
});
});
}
async function DeleteInventoryItems(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.DeleteInventoryItems({
CollectionId: params.CollectionId,
Entity: {
Id: params.TitlePlayerAccountId,
Type: "title_player_account"
},
Item: params.Item,
IdempotencyId: params.IdempotencyId,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
eTag: result.data.ETag,
idempotencyId: result.data.IdempotencyId,
transactionIds: result.data.TransactionIds,
});
});
});
}
async function SubtractInventoryItems(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.SubtractInventoryItems({
Amount: params.Amount,
CollectionId: params.CollectionId,
DurationInSeconds: params.DurationInSeconds,
Entity: {
Id: params.TitlePlayerAccountId,
Type: "title_player_account"
},
Item: params.Item,
IdempotencyId: params.IdempotencyId,
DeleteEmptyStacks: true,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
eTag: result.data.ETag,
idempotencyId: result.data.IdempotencyId,
transactionIds: result.data.TransactionIds,
});
});
});
}
async function UpdateInventoryItems(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.UpdateInventoryItems({
CollectionId: params.CollectionId,
Entity: {
Id: params.TitlePlayerAccountId,
Type: "title_player_account"
},
Item: params.Item,
IdempotencyId: params.IdempotencyId,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
eTag: result.data.ETag,
idempotencyId: result.data.IdempotencyId,
transactionIds: result.data.TransactionIds,
});
});
});
}
async function ExecuteInventoryOperations(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.ExecuteInventoryOperations({
Operations: params.Operations,
Entity: params.Entity,
CollectionId: params.CollectionId,
IdempotencyId: params.IdempotencyId,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
eTag: result.data.ETag,
idempotencyId: result.data.IdempotencyId,
transactionIds: result.data.TransactionIds,
});
});
});
}
async function BanUsers(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.BanUsers({
Bans: params.Bans,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
banData: result.data.BanData,
});
});
});
}
async function RevokeAllBansForUser(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.RevokeAllBansForUser({
PlayFabId: params.PlayFabId,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
banData: result.data.BanData,
});
});
});
}
async function GetUserAccountInfo(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.GetUserAccountInfo({
PlayFabId: params.PlayFabId,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
userInfo: result.data.UserInfo,
});
});
});
}
async function GetUserData(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.GetUserData({
PlayFabId: params.PlayFabId,
Keys: params.Keys,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
data: result.data.Data,
dataVersion: result.data.DataVersion,
});
});
});
}
async function UpdateUserData(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.UpdateUserData({
PlayFabId: params.PlayFabId,
Data: params.Data,
Permission: params.Permission || "Private",
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
dataVersion: result.data.DataVersion,
});
});
});
}
async function SetTitleData(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.SetTitleData({
Key: params.Key,
Value: params.Value,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
});
});
});
}
async function GetTitleData(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.GetTitleData({
Keys: params.Keys,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
data: result.data.Data,
});
});
});
}
async function SetTitleInternalData(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.SetTitleInternalData({
Key: params.Key,
Value: params.Value,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
});
});
});
}
async function GetTitleInternalData(params) {
return new Promise((resolve, reject) => {
PlayFabAdminAPI.GetTitleInternalData({
Keys: params.Keys,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
data: result.data.Data,
});
});
});
}
async function CreateDraftItem(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.CreateDraftItem({
Item: params.Item,
Publish: params.Publish,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
item: result.data.Item,
});
});
});
}
async function UpdateDraftItem(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.UpdateDraftItem({
Item: {
Id: params.ItemId,
...params.Item
},
Publish: params.Publish,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
item: result.data.Item,
});
});
});
}
async function DeleteItem(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.DeleteItem({
Id: params.ItemId,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
});
});
});
}
async function PublishDraftItem(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.PublishDraftItem({
Id: params.ItemId,
ETag: params.ETag,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
});
});
});
}
async function GetItem(params) {
return new Promise((resolve, reject) => {
PlayFabEconomyAPI.GetItem({
Id: params.ItemId,
}, (error, result) => {
if (error) {
reject(JSON.stringify(error, null, 2));
return;
}
resolve({
success: true,
item: result.data.Item,
});
});
});
}
const server = new Server({
name: "playfab-mcp-server",
version: "0.1.0",
}, {
capabilities: {
tools: {},
},
});
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
SEARCH_ITEMS_TOOL,
GET_ALL_SEGMENTS_TOOL,
GET_PLAYERS_IN_SEGMENTS_TOOL,
ADD_INVENTORY_ITEMS_TOOL,
DELETE_INVENTORY_ITEMS_TOOL,
SUBTRACT_INVENTORY_ITEMS_TOOL,
UPDATE_INVENTORY_ITEMS_TOOL,
GET_INVENTORY_ITEMS_TOOL,
GET_INVENTORY_COLLECTION_IDS_TOOL,
GET_TITLE_PLAYER_ACCOUNT_ID_FROM_PLAYFAB_ID_TOOL,
EXECUTE_INVENTORY_OPERATIONS_TOOL,
BAN_USERS_TOOL,
REVOKE_ALL_BANS_FOR_USER_TOOL,
GET_USER_ACCOUNT_INFO_TOOL,
GET_USER_DATA_TOOL,
UPDATE_USER_DATA_TOOL,
SET_TITLE_DATA_TOOL,
GET_TITLE_DATA_TOOL,
SET_TITLE_INTERNAL_DATA_TOOL,
GET_TITLE_INTERNAL_DATA_TOOL,
CREATE_DRAFT_ITEM_TOOL,
UPDATE_DRAFT_ITEM_TOOL,
DELETE_ITEM_TOOL,
PUBLISH_DRAFT_ITEM_TOOL,
GET_ITEM_TOOL,
],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
try {
const result = await new Promise((resolve, reject) => {
PlayFabAuthenticationAPI.GetEntityToken({
CustomTags: {
user: PlayFab.buildIdentifier,
}
}, (error, result) => {
if (error) {
reject(error);
return;
}
let toolPromise;
switch (name) {
case "search_items":
toolPromise = SearchItems(args);
break;
case "get_all_segments":
toolPromise = GetAllSegments(args);
break;
case "get_players_in_segments":
toolPromise = GetPlayersInSegments(args);
break;
case "add_inventory_items":
toolPromise = AddInventoryItems(args);
break;
case "get_inventory_items":
toolPromise = GetInventoryItems(args);
break;
case "get_inventory_collection_ids":
toolPromise = GetInventoryCollectionIds(args);
break;
case "get_title_player_account_id_from_playfab_id":
toolPromise = GetTitlePlayerAccountIdFromPlayFabId(args);
break;
case "delete_inventory_items":
toolPromise = DeleteInventoryItems(args);
break;
case "subtract_inventory_items":
toolPromise = SubtractInventoryItems(args);
break;
case "update_inventory_items":
toolPromise = UpdateInventoryItems(args);
break;
case "execute_inventory_operations":
toolPromise = ExecuteInventoryOperations(args);
break;
case "ban_users":
toolPromise = BanUsers(args);
break;
case "revoke_all_bans_for_user":
toolPromise = RevokeAllBansForUser(args);
break;
case "get_user_account_info":
toolPromise = GetUserAccountInfo(args);
break;
case "get_user_data":
toolPromise = GetUserData(args);
break;
case "update_user_data":
toolPromise = UpdateUserData(args);
break;
case "set_title_data":
toolPromise = SetTitleData(args);
break;
case "get_title_data":
toolPromise = GetTitleData(args);
break;
case "set_title_internal_data":
toolPromise = SetTitleInternalData(args);
break;
case "get_title_internal_data":
toolPromise = GetTitleInternalData(args);
break;
case "create_draft_item":
toolPromise = CreateDraftItem(args);
break;
case "update_draft_item":
toolPromise = UpdateDraftItem(args);
break;
case "delete_item":
toolPromise = DeleteItem(args);
break;
case "publish_draft_item":
toolPromise = PublishDraftItem(args);
break;
case "get_item":
toolPromise = GetItem(args);
break;
default:
reject({
content: [{ type: "text", text: `Unknown tool: ${name}` }],
isError: true,
});
return;
}
toolPromise
.then(toolResult => resolve(toolResult))
.catch(toolError => reject(toolError));
});
});
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
};
}
catch (error) {
return {
content: [{ type: "text", text: `Error occurred: ${error}` }],
isError: true,
};
}
});
async function runServer() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("PlayFab Server running on stdio");
}
runServer().catch((error) => {
console.error("Fatal error running server:", error);
process.exit(1);
});