portkey-admin-mcp 0.3.8 → 0.4.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 CHANGED
@@ -1,22 +1,18 @@
1
1
  <div align="center">
2
2
 
3
- # Portkey Admin MCP Server
3
+ <img src="./assets/portkey-balloon-spin-light.webp" alt="Portkey Admin MCP icon" width="180" height="180">
4
4
 
5
- <picture>
6
- <source media="(prefers-color-scheme: dark)" srcset="https://readme-typing-svg.demolab.com?font=Fira+Code&pause=1000&color=FFFFFF&center=true&vCenter=true&width=500&lines=150+tools+for+Portkey+Admin+API;Prompts%2C+Configs%2C+Analytics;Full+MCP+Protocol+1.0+Server">
7
- <source media="(prefers-color-scheme: light)" srcset="https://readme-typing-svg.demolab.com?font=Fira+Code&pause=1000&color=000000&center=true&vCenter=true&width=500&lines=150+tools+for+Portkey+Admin+API;Prompts%2C+Configs%2C+Analytics;Full+MCP+Protocol+1.0+Server">
8
- <img src="https://readme-typing-svg.demolab.com?font=Fira+Code&pause=1000&color=000000&center=true&vCenter=true&width=500&lines=150+tools+for+Portkey+Admin+API;Prompts%2C+Configs%2C+Analytics;Full+MCP+Protocol+1.0+Server" alt="Typing SVG">
9
- </picture>
5
+ # Portkey Admin MCP Server
10
6
 
11
7
  MCP server for the [Portkey](https://portkey.ai/) Admin API. Manage prompts, configs, analytics, API keys, and more from any MCP client.
12
8
 
13
9
  <a href="https://www.npmjs.com/package/portkey-admin-mcp"><img src="https://img.shields.io/npm/v/portkey-admin-mcp.svg" alt="npm version"></a>
14
10
  <a href="https://www.npmjs.com/package/portkey-admin-mcp"><img src="https://img.shields.io/npm/dm/portkey-admin-mcp.svg" alt="npm downloads"></a>
15
11
  <a href="https://github.com/CodesWhat/portkey-admin-mcp/actions/workflows/ci.yml"><img src="https://github.com/CodesWhat/portkey-admin-mcp/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
16
- <a href="https://nodejs.org/"><img src="https://img.shields.io/badge/node-%3E%3D20-brightgreen.svg" alt="Node.js"></a>
12
+ <a href="https://nodejs.org/"><img src="https://img.shields.io/badge/node-%3E%3D24-brightgreen.svg" alt="Node.js"></a>
17
13
  <a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
18
14
  <a href="https://github.com/punkpeye/awesome-mcp-servers"><img src="https://awesome.re/mentioned-badge.svg" alt="Mentioned in Awesome MCP Servers"></a>
19
- <a href="https://lobehub.com/mcp/codeswhat-portkey-admin-mcp"><img src="https://lobehub.com/badge/mcp/codeswhat-portkey-admin-mcp?style=flat" alt="MCP Badge"></a>
15
+ [![MCP Badge](https://lobehub.com/badge/mcp-full/codeswhat-portkey-admin-mcp?theme=light)](https://lobehub.com/mcp/codeswhat-portkey-admin-mcp)
20
16
 
21
17
  <a href="https://glama.ai/mcp/servers/CodesWhat/portkey-admin-mcp"><img src="https://glama.ai/mcp/servers/CodesWhat/portkey-admin-mcp/badges/card.svg" alt="portkey-admin-mcp MCP server"></a>
22
18
 
@@ -220,6 +216,7 @@ Key points:
220
216
  docker build -t portkey-admin-mcp .
221
217
  docker run \
222
218
  -e PORTKEY_API_KEY=your_key \
219
+ -e MCP_TRANSPORT=http \
223
220
  -e MCP_HOST=0.0.0.0 \
224
221
  -e MCP_PORT=3000 \
225
222
  -e MCP_AUTH_MODE=bearer \
package/build/index.js CHANGED
@@ -2797,8 +2797,14 @@ function registerConfigsTools(server, service) {
2797
2797
  );
2798
2798
  server.tool(
2799
2799
  "update_config",
2800
- "Update a config by slug and create a new version. Only provided fields change; name and status are editable, while the slug stays fixed. Use list_config_versions if you need history first.",
2800
+ "Update a config by slug; every call creates a new config version rather than overwriting, so earlier versions stay recoverable via list_config_versions. Only provided fields change: name, status, and all routing/cache/retry settings (cache_mode, cache_max_age, retry_attempts, retry_on_status_codes, strategy_mode, targets) are editable, while the slug stays fixed. Changes apply immediately to every API key and prompt referencing the config; get the slug from list_configs and review current settings with get_config before editing. Returns the config id, slug, and updated config payload.",
2801
2801
  CONFIGS_TOOL_SCHEMAS.updateConfig,
2802
+ {
2803
+ readOnlyHint: false,
2804
+ destructiveHint: false,
2805
+ idempotentHint: false,
2806
+ openWorldHint: false
2807
+ },
2802
2808
  async (params) => {
2803
2809
  const config = buildConfigPayload(params);
2804
2810
  const updateData = {};
@@ -2897,7 +2903,7 @@ var GUARDRAILS_TOOL_SCHEMAS = {
2897
2903
  listGuardrails: {
2898
2904
  workspace_id: z5.string().optional().describe("Filter guardrails by workspace ID"),
2899
2905
  organisation_id: z5.string().optional().describe("Filter guardrails by organization ID"),
2900
- page_size: z5.coerce.number().min(1).max(1e3).optional().describe("Number of items per page (1-1000, default: 100)"),
2906
+ page_size: z5.coerce.number().min(1).max(100).optional().describe("Number of items per page (1-100, default: 100)"),
2901
2907
  current_page: z5.coerce.number().positive().optional().describe("Page number for pagination")
2902
2908
  },
2903
2909
  getGuardrail: {
@@ -3262,8 +3268,14 @@ function registerIntegrationsTools(server, service) {
3262
3268
  );
3263
3269
  server.tool(
3264
3270
  "create_integration",
3265
- "Create an org-level provider integration. Some backends need provider-specific fields, and the new integration becomes the source for downstream providers and workspace access. Returns the new integration id and slug.",
3271
+ "Create an org-level provider integration that stores an AI provider's API key and becomes the source workspace providers (create_provider) build on. ai_provider_id takes a provider identifier such as 'openai' or 'anthropic'; some backends (azure-openai, aws-bedrock, vertex-ai) need provider-specific fields, and the key is write-only afterwards (get_integration shows it masked). Provision models and workspace access afterwards with update_integration_models and update_integration_workspaces. Returns the new integration id and slug.",
3266
3272
  INTEGRATIONS_TOOL_SCHEMAS.createIntegration,
3273
+ {
3274
+ readOnlyHint: false,
3275
+ destructiveHint: false,
3276
+ idempotentHint: false,
3277
+ openWorldHint: false
3278
+ },
3267
3279
  async (params) => {
3268
3280
  const result = await service.integrations.createIntegration({
3269
3281
  name: params.name,
@@ -3323,8 +3335,14 @@ function registerIntegrationsTools(server, service) {
3323
3335
  );
3324
3336
  server.tool(
3325
3337
  "update_integration",
3326
- "Update an integration's name, key, or provider-specific config. Key and config changes take effect immediately and can disrupt dependent providers or live requests.",
3338
+ "Update an org-level integration's name, description, API key, or provider-specific config by slug. Only provided fields change and the update is in place (no version history); key and config changes take effect immediately and can disrupt dependent providers or live requests, so review current settings with get_integration first. Get the slug from list_integrations; model provisioning and workspace access are managed separately via update_integration_models and update_integration_workspaces. Returns a success flag.",
3327
3339
  INTEGRATIONS_TOOL_SCHEMAS.updateIntegration,
3340
+ {
3341
+ readOnlyHint: false,
3342
+ destructiveHint: true,
3343
+ idempotentHint: true,
3344
+ openWorldHint: false
3345
+ },
3328
3346
  async (params) => {
3329
3347
  const result = await service.integrations.updateIntegration(params.slug, {
3330
3348
  name: params.name,
@@ -5131,8 +5149,14 @@ function registerMcpIntegrationsTools(server, service) {
5131
5149
  );
5132
5150
  server.tool(
5133
5151
  "update_mcp_integration_capabilities",
5134
- "Bulk enable or disable capabilities on an MCP integration. Changes take effect immediately for connected users and hide or expose the selected tools, resources, and prompts; use list_mcp_integration_capabilities first if you need the current state.",
5152
+ "Bulk enable or disable capabilities (tools, prompts, resources) on an MCP integration. A reversible toggle, not a deletion: only the capabilities named in the array change state, the change hides or exposes them immediately for connected users, and re-running with enabled flipped restores them. Source the integration id from list_mcp_integrations and current capability names, types, and states from list_mcp_integration_capabilities. Returns a success confirmation message.",
5135
5153
  MCP_INTEGRATIONS_TOOL_SCHEMAS.updateMcpIntegrationCapabilities,
5154
+ {
5155
+ readOnlyHint: false,
5156
+ destructiveHint: false,
5157
+ idempotentHint: true,
5158
+ openWorldHint: false
5159
+ },
5136
5160
  async (params) => {
5137
5161
  await service.mcpIntegrations.updateMcpIntegrationCapabilities(
5138
5162
  params.id,
@@ -5177,8 +5201,14 @@ function registerMcpIntegrationsTools(server, service) {
5177
5201
  );
5178
5202
  server.tool(
5179
5203
  "update_mcp_integration_workspaces",
5180
- "Grant or revoke workspace access to an MCP integration in bulk. Changes take effect immediately for all users in the selected workspaces; use list_mcp_integration_workspaces first to review the current access state.",
5204
+ "Grant or revoke workspace access to an MCP integration in bulk. Reversible: only the workspaces listed change, access applies or is removed immediately for all users in those workspaces, and re-running with enabled flipped undoes a change. Source the integration id from list_mcp_integrations, workspace ids from list_workspaces, and the current access state from list_mcp_integration_workspaces. Returns a success confirmation message.",
5181
5205
  MCP_INTEGRATIONS_TOOL_SCHEMAS.updateMcpIntegrationWorkspaces,
5206
+ {
5207
+ readOnlyHint: false,
5208
+ destructiveHint: false,
5209
+ idempotentHint: true,
5210
+ openWorldHint: false
5211
+ },
5182
5212
  async (params) => {
5183
5213
  await service.mcpIntegrations.updateMcpIntegrationWorkspaces(params.id, {
5184
5214
  workspaces: params.workspaces
@@ -6375,8 +6405,14 @@ function registerPromptsTools(server, service) {
6375
6405
  );
6376
6406
  server.tool(
6377
6407
  "migrate_prompt",
6378
- "Create or update a prompt in one idempotent step for CI/CD and prompt-as-code flows. Finds existing prompts by name within the collection, stores app/env in template_metadata, and supports dry_run for safe preflight checks.",
6408
+ "Create or update a prompt in one idempotent step for CI/CD and prompt-as-code flows, unlike create_prompt which always makes a new prompt. Looks up the prompt by name within collection_id: if found it adds a new version (nothing is overwritten), otherwise it creates the prompt; dry_run reports what would happen without changing anything. Stores app/env in template_metadata; get collection_id from list_collections. Returns the action taken (created or updated), dry_run flag, prompt id, slug, and version id.",
6379
6409
  PROMPTS_TOOL_SCHEMAS.migratePrompt,
6410
+ {
6411
+ readOnlyHint: false,
6412
+ destructiveHint: false,
6413
+ idempotentHint: true,
6414
+ openWorldHint: false
6415
+ },
6380
6416
  async (params) => {
6381
6417
  const templateString = normalizePromptTemplateString(params);
6382
6418
  if (templateString === void 0) {
@@ -6891,7 +6927,7 @@ var USERS_TOOL_SCHEMAS = {
6891
6927
  cost_max: z18.coerce.number().positive().optional().describe("Maximum cost in cents to filter by"),
6892
6928
  status_code: z18.string().optional().describe("Filter by specific HTTP status codes (comma-separated)"),
6893
6929
  virtual_keys: z18.string().optional().describe("Filter by specific virtual key slugs (comma-separated)"),
6894
- page_size: z18.coerce.number().positive().optional().describe("Number of results per page (for pagination)")
6930
+ page_size: z18.coerce.number().positive().max(100).optional().describe("Number of results per page (max 100)")
6895
6931
  },
6896
6932
  getUser: {
6897
6933
  user_id: z18.string().describe("The user ID to retrieve")
@@ -7145,9 +7181,7 @@ function registerUsersTools(server, service) {
7145
7181
  import { z as z19 } from "zod";
7146
7182
  var WORKSPACES_TOOL_SCHEMAS = {
7147
7183
  listWorkspaces: {
7148
- page_size: z19.coerce.number().positive().optional().describe(
7149
- "Number of workspaces to return per page (default varies by endpoint)"
7150
- ),
7184
+ page_size: z19.coerce.number().positive().max(100).optional().describe("Number of workspaces to return per page (max 100)"),
7151
7185
  current_page: z19.coerce.number().positive().optional().describe("Page number to retrieve when results are paginated")
7152
7186
  },
7153
7187
  getWorkspace: {
package/build/server.js CHANGED
@@ -2811,8 +2811,14 @@ function registerConfigsTools(server, service) {
2811
2811
  );
2812
2812
  server.tool(
2813
2813
  "update_config",
2814
- "Update a config by slug and create a new version. Only provided fields change; name and status are editable, while the slug stays fixed. Use list_config_versions if you need history first.",
2814
+ "Update a config by slug; every call creates a new config version rather than overwriting, so earlier versions stay recoverable via list_config_versions. Only provided fields change: name, status, and all routing/cache/retry settings (cache_mode, cache_max_age, retry_attempts, retry_on_status_codes, strategy_mode, targets) are editable, while the slug stays fixed. Changes apply immediately to every API key and prompt referencing the config; get the slug from list_configs and review current settings with get_config before editing. Returns the config id, slug, and updated config payload.",
2815
2815
  CONFIGS_TOOL_SCHEMAS.updateConfig,
2816
+ {
2817
+ readOnlyHint: false,
2818
+ destructiveHint: false,
2819
+ idempotentHint: false,
2820
+ openWorldHint: false
2821
+ },
2816
2822
  async (params) => {
2817
2823
  const config = buildConfigPayload(params);
2818
2824
  const updateData = {};
@@ -2911,7 +2917,7 @@ var GUARDRAILS_TOOL_SCHEMAS = {
2911
2917
  listGuardrails: {
2912
2918
  workspace_id: z5.string().optional().describe("Filter guardrails by workspace ID"),
2913
2919
  organisation_id: z5.string().optional().describe("Filter guardrails by organization ID"),
2914
- page_size: z5.coerce.number().min(1).max(1e3).optional().describe("Number of items per page (1-1000, default: 100)"),
2920
+ page_size: z5.coerce.number().min(1).max(100).optional().describe("Number of items per page (1-100, default: 100)"),
2915
2921
  current_page: z5.coerce.number().positive().optional().describe("Page number for pagination")
2916
2922
  },
2917
2923
  getGuardrail: {
@@ -3276,8 +3282,14 @@ function registerIntegrationsTools(server, service) {
3276
3282
  );
3277
3283
  server.tool(
3278
3284
  "create_integration",
3279
- "Create an org-level provider integration. Some backends need provider-specific fields, and the new integration becomes the source for downstream providers and workspace access. Returns the new integration id and slug.",
3285
+ "Create an org-level provider integration that stores an AI provider's API key and becomes the source workspace providers (create_provider) build on. ai_provider_id takes a provider identifier such as 'openai' or 'anthropic'; some backends (azure-openai, aws-bedrock, vertex-ai) need provider-specific fields, and the key is write-only afterwards (get_integration shows it masked). Provision models and workspace access afterwards with update_integration_models and update_integration_workspaces. Returns the new integration id and slug.",
3280
3286
  INTEGRATIONS_TOOL_SCHEMAS.createIntegration,
3287
+ {
3288
+ readOnlyHint: false,
3289
+ destructiveHint: false,
3290
+ idempotentHint: false,
3291
+ openWorldHint: false
3292
+ },
3281
3293
  async (params) => {
3282
3294
  const result = await service.integrations.createIntegration({
3283
3295
  name: params.name,
@@ -3337,8 +3349,14 @@ function registerIntegrationsTools(server, service) {
3337
3349
  );
3338
3350
  server.tool(
3339
3351
  "update_integration",
3340
- "Update an integration's name, key, or provider-specific config. Key and config changes take effect immediately and can disrupt dependent providers or live requests.",
3352
+ "Update an org-level integration's name, description, API key, or provider-specific config by slug. Only provided fields change and the update is in place (no version history); key and config changes take effect immediately and can disrupt dependent providers or live requests, so review current settings with get_integration first. Get the slug from list_integrations; model provisioning and workspace access are managed separately via update_integration_models and update_integration_workspaces. Returns a success flag.",
3341
3353
  INTEGRATIONS_TOOL_SCHEMAS.updateIntegration,
3354
+ {
3355
+ readOnlyHint: false,
3356
+ destructiveHint: true,
3357
+ idempotentHint: true,
3358
+ openWorldHint: false
3359
+ },
3342
3360
  async (params) => {
3343
3361
  const result = await service.integrations.updateIntegration(params.slug, {
3344
3362
  name: params.name,
@@ -5145,8 +5163,14 @@ function registerMcpIntegrationsTools(server, service) {
5145
5163
  );
5146
5164
  server.tool(
5147
5165
  "update_mcp_integration_capabilities",
5148
- "Bulk enable or disable capabilities on an MCP integration. Changes take effect immediately for connected users and hide or expose the selected tools, resources, and prompts; use list_mcp_integration_capabilities first if you need the current state.",
5166
+ "Bulk enable or disable capabilities (tools, prompts, resources) on an MCP integration. A reversible toggle, not a deletion: only the capabilities named in the array change state, the change hides or exposes them immediately for connected users, and re-running with enabled flipped restores them. Source the integration id from list_mcp_integrations and current capability names, types, and states from list_mcp_integration_capabilities. Returns a success confirmation message.",
5149
5167
  MCP_INTEGRATIONS_TOOL_SCHEMAS.updateMcpIntegrationCapabilities,
5168
+ {
5169
+ readOnlyHint: false,
5170
+ destructiveHint: false,
5171
+ idempotentHint: true,
5172
+ openWorldHint: false
5173
+ },
5150
5174
  async (params) => {
5151
5175
  await service.mcpIntegrations.updateMcpIntegrationCapabilities(
5152
5176
  params.id,
@@ -5191,8 +5215,14 @@ function registerMcpIntegrationsTools(server, service) {
5191
5215
  );
5192
5216
  server.tool(
5193
5217
  "update_mcp_integration_workspaces",
5194
- "Grant or revoke workspace access to an MCP integration in bulk. Changes take effect immediately for all users in the selected workspaces; use list_mcp_integration_workspaces first to review the current access state.",
5218
+ "Grant or revoke workspace access to an MCP integration in bulk. Reversible: only the workspaces listed change, access applies or is removed immediately for all users in those workspaces, and re-running with enabled flipped undoes a change. Source the integration id from list_mcp_integrations, workspace ids from list_workspaces, and the current access state from list_mcp_integration_workspaces. Returns a success confirmation message.",
5195
5219
  MCP_INTEGRATIONS_TOOL_SCHEMAS.updateMcpIntegrationWorkspaces,
5220
+ {
5221
+ readOnlyHint: false,
5222
+ destructiveHint: false,
5223
+ idempotentHint: true,
5224
+ openWorldHint: false
5225
+ },
5196
5226
  async (params) => {
5197
5227
  await service.mcpIntegrations.updateMcpIntegrationWorkspaces(params.id, {
5198
5228
  workspaces: params.workspaces
@@ -6389,8 +6419,14 @@ function registerPromptsTools(server, service) {
6389
6419
  );
6390
6420
  server.tool(
6391
6421
  "migrate_prompt",
6392
- "Create or update a prompt in one idempotent step for CI/CD and prompt-as-code flows. Finds existing prompts by name within the collection, stores app/env in template_metadata, and supports dry_run for safe preflight checks.",
6422
+ "Create or update a prompt in one idempotent step for CI/CD and prompt-as-code flows, unlike create_prompt which always makes a new prompt. Looks up the prompt by name within collection_id: if found it adds a new version (nothing is overwritten), otherwise it creates the prompt; dry_run reports what would happen without changing anything. Stores app/env in template_metadata; get collection_id from list_collections. Returns the action taken (created or updated), dry_run flag, prompt id, slug, and version id.",
6393
6423
  PROMPTS_TOOL_SCHEMAS.migratePrompt,
6424
+ {
6425
+ readOnlyHint: false,
6426
+ destructiveHint: false,
6427
+ idempotentHint: true,
6428
+ openWorldHint: false
6429
+ },
6394
6430
  async (params) => {
6395
6431
  const templateString = normalizePromptTemplateString(params);
6396
6432
  if (templateString === void 0) {
@@ -6905,7 +6941,7 @@ var USERS_TOOL_SCHEMAS = {
6905
6941
  cost_max: z18.coerce.number().positive().optional().describe("Maximum cost in cents to filter by"),
6906
6942
  status_code: z18.string().optional().describe("Filter by specific HTTP status codes (comma-separated)"),
6907
6943
  virtual_keys: z18.string().optional().describe("Filter by specific virtual key slugs (comma-separated)"),
6908
- page_size: z18.coerce.number().positive().optional().describe("Number of results per page (for pagination)")
6944
+ page_size: z18.coerce.number().positive().max(100).optional().describe("Number of results per page (max 100)")
6909
6945
  },
6910
6946
  getUser: {
6911
6947
  user_id: z18.string().describe("The user ID to retrieve")
@@ -7159,9 +7195,7 @@ function registerUsersTools(server, service) {
7159
7195
  import { z as z19 } from "zod";
7160
7196
  var WORKSPACES_TOOL_SCHEMAS = {
7161
7197
  listWorkspaces: {
7162
- page_size: z19.coerce.number().positive().optional().describe(
7163
- "Number of workspaces to return per page (default varies by endpoint)"
7164
- ),
7198
+ page_size: z19.coerce.number().positive().max(100).optional().describe("Number of workspaces to return per page (max 100)"),
7165
7199
  current_page: z19.coerce.number().positive().optional().describe("Page number to retrieve when results are paginated")
7166
7200
  },
7167
7201
  getWorkspace: {
package/package.json CHANGED
@@ -22,11 +22,13 @@
22
22
  "knip": "knip",
23
23
  "ci": "npm run lint && npm run knip && npm run typecheck && npm run test && npm run build && npm run test:e2e && npm run verify:readme-tools",
24
24
  "start:http": "node build/server.js",
25
+ "generate:lobehub-tools": "npm run build && node scripts/generate-lobehub-tools.mjs",
26
+ "publish:lobehub": "npm run generate:lobehub-tools && npx -y @lobehub/market-cli plugin publish --dir \"$PWD\"",
25
27
  "prepare": "[ -n \"$CI\" ] || [ -f /.dockerenv ] || [ ! -d .git ] || lefthook install",
26
28
  "prepublishOnly": "npm run ci"
27
29
  },
28
30
  "engines": {
29
- "node": ">=20"
31
+ "node": ">=24"
30
32
  },
31
33
  "files": [
32
34
  "build"
@@ -42,7 +44,7 @@
42
44
  },
43
45
  "name": "portkey-admin-mcp",
44
46
  "mcpName": "io.github.CodesWhat/portkey-admin-mcp",
45
- "version": "0.3.8",
47
+ "version": "0.4.1",
46
48
  "main": "build/index.js",
47
49
  "keywords": [
48
50
  "mcp",