@ampless/mcp-server 1.0.0-alpha.14 → 1.0.0-alpha.16

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.ja.md CHANGED
@@ -130,9 +130,14 @@ AI に投稿本文を生成させる場合は、markdown を指定するのが
130
130
  ampless-mcp [options]
131
131
 
132
132
  --outputs <path> amplify_outputs.json へのパス(AMPLESS_MCP_OUTPUTS でも指定可)
133
+ --site-name <name> コンテキスト用のサイト表示名(AMPLESS_MCP_SITE_NAME でも指定可、例: "mysite.net")
134
+ --site-url <url> ツール結果に含めるサイト URL(AMPLESS_MCP_SITE_URL でも指定可、省略可)
135
+ --environment <env> デプロイ環境: prod | stg | dev(AMPLESS_MCP_ENVIRONMENT でも指定可、デフォルト "dev")
133
136
  --site-id <id> クエリのデフォルト siteId(AMPLESS_MCP_SITE_ID でも指定可、デフォルト "default")
134
137
  ```
135
138
 
139
+ `--site-name` を設定すると、MCP クライアントに表示されるサーバー名にサイトと環境が含まれ(例: `@ampless/mcp-server [mysite.net/prod]`)、ツールの description に `[mysite.net / prod]` が前置され、結果が `{ site, result }` でラップされます。`--environment prod` 設定時、破壊的ツール(`delete_post`、`delete_static_file`、`upload_static_bundle`)の呼び出しにはサイト名と一致する `confirmSite` 引数が必要です。
140
+
136
141
  必須の環境変数:
137
142
 
138
143
  ```
package/README.md CHANGED
@@ -130,9 +130,14 @@ If you're letting the AI generate post bodies, asking for markdown is usually ea
130
130
  ampless-mcp [options]
131
131
 
132
132
  --outputs <path> Path to amplify_outputs.json (also AMPLESS_MCP_OUTPUTS)
133
+ --site-name <name> Site display name for context (also AMPLESS_MCP_SITE_NAME; e.g. "mysite.net")
134
+ --site-url <url> Site URL included in tool results (also AMPLESS_MCP_SITE_URL; optional)
135
+ --environment <env> Deployment environment: prod | stg | dev (also AMPLESS_MCP_ENVIRONMENT; default "dev")
133
136
  --site-id <id> Default siteId for queries (also AMPLESS_MCP_SITE_ID; default "default")
134
137
  ```
135
138
 
139
+ When `--site-name` is set, the server name shown in MCP clients includes the site and environment (e.g. `@ampless/mcp-server [mysite.net/prod]`), tool descriptions are prefixed with `[mysite.net / prod]`, and results are wrapped in `{ site, result }`. Destructive tools (`delete_post`, `delete_static_file`, `upload_static_bundle`) require a `confirmSite` argument equal to the site name when `--environment prod` is set.
140
+
136
141
  Required env:
137
142
 
138
143
  ```
@@ -894,7 +894,8 @@ var tools = [
894
894
  name: "delete_post",
895
895
  description: "Delete a post by postId. Also drops associated PostTag index entries.",
896
896
  inputSchema: deletePostSchema,
897
- handler: (args, ctx) => deletePost(ctx.graphql, args)
897
+ handler: (args, ctx) => deletePost(ctx.graphql, args),
898
+ destructive: true
898
899
  },
899
900
  {
900
901
  name: "upload_media",
@@ -916,7 +917,8 @@ var tools = [
916
917
  ctx.graphql,
917
918
  ctx.storage(),
918
919
  args
919
- )
920
+ ),
921
+ destructive: true
920
922
  },
921
923
  {
922
924
  name: "upload_static_file",
@@ -934,7 +936,8 @@ var tools = [
934
936
  handler: (args, ctx) => deleteStaticFile(
935
937
  ctx.storage(),
936
938
  args
937
- )
939
+ ),
940
+ destructive: true
938
941
  },
939
942
  {
940
943
  name: "commit_static_post",
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  } from "./chunk-USNYVOYF.js";
25
25
  import {
26
26
  tools
27
- } from "./chunk-K4GTND6P.js";
27
+ } from "./chunk-HVSLKTKC.js";
28
28
  import {
29
29
  eventStreamSerdeProvider2 as eventStreamSerdeProvider,
30
30
  resolveEventStreamSerdeConfig
@@ -10898,6 +10898,22 @@ function parseArgs(argv) {
10898
10898
  out.outputs = argv[++i2];
10899
10899
  } else if (arg.startsWith("--outputs=")) {
10900
10900
  out.outputs = arg.slice("--outputs=".length);
10901
+ } else if (arg === "--site-name") {
10902
+ out.siteName = argv[++i2];
10903
+ } else if (arg.startsWith("--site-name=")) {
10904
+ out.siteName = arg.slice("--site-name=".length);
10905
+ } else if (arg === "--site-url") {
10906
+ out.siteUrl = argv[++i2];
10907
+ } else if (arg.startsWith("--site-url=")) {
10908
+ out.siteUrl = arg.slice("--site-url=".length);
10909
+ } else if (arg === "--environment") {
10910
+ out.environment = argv[++i2];
10911
+ } else if (arg.startsWith("--environment=")) {
10912
+ out.environment = arg.slice("--environment=".length);
10913
+ } else if (arg === "--site-id") {
10914
+ out.siteId = argv[++i2];
10915
+ } else if (arg.startsWith("--site-id=")) {
10916
+ out.siteId = arg.slice("--site-id=".length);
10901
10917
  }
10902
10918
  }
10903
10919
  return out;
@@ -10934,10 +10950,27 @@ async function loadConfig2(argv = process.argv.slice(2)) {
10934
10950
  if (!outputs.data?.url) {
10935
10951
  throw new Error("mcp-server: outputs.data.url missing");
10936
10952
  }
10953
+ const siteName = args.siteName ?? process.env.AMPLESS_MCP_SITE_NAME;
10954
+ let site;
10955
+ if (siteName) {
10956
+ const rawEnv = args.environment ?? process.env.AMPLESS_MCP_ENVIRONMENT ?? "dev";
10957
+ if (rawEnv !== "prod" && rawEnv !== "stg" && rawEnv !== "dev") {
10958
+ throw new Error(
10959
+ `mcp-server: --environment must be one of prod, stg, dev (got "${rawEnv}")`
10960
+ );
10961
+ }
10962
+ site = {
10963
+ name: siteName,
10964
+ url: args.siteUrl ?? process.env.AMPLESS_MCP_SITE_URL,
10965
+ environment: rawEnv,
10966
+ siteId: args.siteId ?? process.env.AMPLESS_MCP_SITE_ID ?? "default"
10967
+ };
10968
+ }
10937
10969
  return {
10938
10970
  outputs,
10939
10971
  email: requireEnv("AMPLESS_MCP_EMAIL"),
10940
- password: requireEnv("AMPLESS_MCP_PASSWORD")
10972
+ password: requireEnv("AMPLESS_MCP_PASSWORD"),
10973
+ site
10941
10974
  };
10942
10975
  }
10943
10976
 
@@ -32167,6 +32200,60 @@ var StorageClient = class {
32167
32200
  }
32168
32201
  };
32169
32202
 
32203
+ // src/site.ts
32204
+ function buildServerName(site) {
32205
+ if (!site) return "@ampless/mcp-server";
32206
+ return `@ampless/mcp-server [${site.name}/${site.environment}]`;
32207
+ }
32208
+ function decorateToolListing(tools2, site) {
32209
+ if (!site) {
32210
+ return tools2.map((t8) => ({
32211
+ name: t8.name,
32212
+ description: t8.description,
32213
+ inputSchema: t8.inputSchema
32214
+ }));
32215
+ }
32216
+ return tools2.map((t8) => {
32217
+ const isProdDestructive = t8.destructive === true && site.environment === "prod";
32218
+ const envLabel = site.environment === "prod" ? "PROD" : site.environment;
32219
+ let description;
32220
+ if (isProdDestructive) {
32221
+ description = `[${site.name} / ${envLabel} \u2014 destructive] ${t8.description} Requires confirmSite: "${site.name}".`;
32222
+ } else if (t8.destructive) {
32223
+ description = `[${site.name} / ${envLabel} \u2014 destructive] ${t8.description}`;
32224
+ } else {
32225
+ description = `[${site.name} / ${site.environment}] ${t8.description}`;
32226
+ }
32227
+ let inputSchema = t8.inputSchema;
32228
+ if (isProdDestructive) {
32229
+ const cloned = JSON.parse(JSON.stringify(t8.inputSchema));
32230
+ const props = cloned.properties ?? {};
32231
+ props["confirmSite"] = {
32232
+ type: "string",
32233
+ const: site.name,
32234
+ description: `Must equal "${site.name}" to confirm this destructive operation on the production site.`
32235
+ };
32236
+ cloned.properties = props;
32237
+ const existing = cloned.required ?? [];
32238
+ cloned.required = ["confirmSite", ...existing];
32239
+ inputSchema = cloned;
32240
+ }
32241
+ return { name: t8.name, description, inputSchema };
32242
+ });
32243
+ }
32244
+ function assertConfirmSite(tool, args, site) {
32245
+ if (!site || site.environment !== "prod" || !tool.destructive) return;
32246
+ if (args["confirmSite"] !== site.name) {
32247
+ throw new Error(
32248
+ `confirmSite mismatch: expected "${site.name}", got ${JSON.stringify(args["confirmSite"])}. Pass confirmSite: "${site.name}" to confirm this destructive operation on the production site.`
32249
+ );
32250
+ }
32251
+ }
32252
+ function wrapResult(result, site) {
32253
+ if (!site) return result;
32254
+ return { site, result };
32255
+ }
32256
+
32170
32257
  // src/server.ts
32171
32258
  async function startServer(config2) {
32172
32259
  const auth = new CognitoAuth(config2);
@@ -32179,18 +32266,15 @@ async function startServer(config2) {
32179
32266
  };
32180
32267
  const ctx = {
32181
32268
  graphql,
32182
- storage
32269
+ storage,
32270
+ site: config2.site
32183
32271
  };
32184
32272
  const server = new Server(
32185
- { name: "@ampless/mcp-server", version: "0.0.1" },
32273
+ { name: buildServerName(config2.site), version: "0.0.1" },
32186
32274
  { capabilities: { tools: {} } }
32187
32275
  );
32188
32276
  server.setRequestHandler(ListToolsRequestSchema, async () => ({
32189
- tools: tools.map((t8) => ({
32190
- name: t8.name,
32191
- description: t8.description,
32192
- inputSchema: t8.inputSchema
32193
- }))
32277
+ tools: decorateToolListing(tools, config2.site)
32194
32278
  }));
32195
32279
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
32196
32280
  const tool = tools.find((t8) => t8.name === request.params.name);
@@ -32198,12 +32282,12 @@ async function startServer(config2) {
32198
32282
  throw new Error(`Unknown tool: ${request.params.name}`);
32199
32283
  }
32200
32284
  try {
32201
- const result = await tool.handler(
32202
- request.params.arguments ?? {},
32203
- ctx
32204
- );
32285
+ const args = { ...request.params.arguments ?? {} };
32286
+ assertConfirmSite(tool, args, config2.site);
32287
+ delete args["confirmSite"];
32288
+ const result = await tool.handler(args, ctx);
32205
32289
  return {
32206
- content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
32290
+ content: [{ type: "text", text: JSON.stringify(wrapResult(result, config2.site), null, 2) }]
32207
32291
  };
32208
32292
  } catch (err) {
32209
32293
  return {
@@ -1,5 +1,12 @@
1
1
  import { BundleExtractResult } from 'ampless';
2
2
 
3
+ interface ResolvedSite {
4
+ name: string;
5
+ url?: string;
6
+ environment: 'prod' | 'stg' | 'dev';
7
+ siteId: string;
8
+ }
9
+
3
10
  /**
4
11
  * Abstract contracts each tool handler depends on. Concrete
5
12
  * implementations live elsewhere:
@@ -48,6 +55,7 @@ interface StorageClient {
48
55
  interface ToolContext {
49
56
  graphql: GraphqlClient;
50
57
  storage: () => StorageClient;
58
+ site?: ResolvedSite;
51
59
  }
52
60
 
53
61
  /**
@@ -100,6 +108,7 @@ interface ToolDefinition {
100
108
  description: string;
101
109
  inputSchema: Record<string, unknown>;
102
110
  handler: (args: Record<string, unknown>, ctx: ToolContext) => Promise<unknown>;
111
+ destructive?: boolean;
103
112
  }
104
113
  declare const tools: ToolDefinition[];
105
114
  /**
@@ -5,7 +5,7 @@ import {
5
5
  extractZipFromBuffer,
6
6
  getTools,
7
7
  tools
8
- } from "../chunk-K4GTND6P.js";
8
+ } from "../chunk-HVSLKTKC.js";
9
9
  import "../chunk-LMMQX4CK.js";
10
10
  export {
11
11
  decodeUtf8,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ampless/mcp-server",
3
- "version": "1.0.0-alpha.14",
3
+ "version": "1.0.0-alpha.16",
4
4
  "description": "MCP server for ampless — lets AI agents (Claude Desktop, Cursor, Claude Code) read and write posts and media via AppSync",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  "@aws-sdk/client-s3": "^3.1053.0",
37
37
  "amazon-cognito-identity-js": "^6.3.16",
38
38
  "fflate": "^0.8.3",
39
- "ampless": "1.0.0-alpha.13"
39
+ "ampless": "1.0.0-alpha.14"
40
40
  },
41
41
  "keywords": [
42
42
  "ampless",