create-ampless 0.2.0-alpha.20 → 0.2.0-alpha.22
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/dist/index.js +74 -2
- package/dist/templates/_shared/AGENTS.ja.md +1 -5
- package/dist/templates/_shared/AGENTS.md +1 -5
- package/dist/templates/_shared/README.ja.md +0 -3
- package/dist/templates/_shared/README.md +0 -3
- package/dist/templates/_shared/amplify/backend.ts +2 -0
- package/dist/templates/_shared/amplify/functions/mcp-handler/handler.ts +1 -0
- package/dist/templates/_shared/amplify/functions/mcp-handler/resource.ts +11 -0
- package/dist/templates/_shared/package.json +2 -2
- package/package.json +1 -1
- package/dist/templates/_shared/app/api/admin/mcp-tokens/route.ts +0 -6
- package/dist/templates/_shared/app/api/mcp/route.ts +0 -9
package/dist/index.js
CHANGED
|
@@ -1761,6 +1761,14 @@ import { join as join3, relative, extname as extname2, dirname } from "path";
|
|
|
1761
1761
|
import { log as log3, outro } from "@clack/prompts";
|
|
1762
1762
|
import pc3 from "picocolors";
|
|
1763
1763
|
import { execa as execa4 } from "execa";
|
|
1764
|
+
var AMPLESS_MANAGED_APP_PATHS = [
|
|
1765
|
+
"app/(admin)/admin",
|
|
1766
|
+
"app/api/admin",
|
|
1767
|
+
"app/api/media",
|
|
1768
|
+
"app/api/mcp",
|
|
1769
|
+
"app/login",
|
|
1770
|
+
"app/site"
|
|
1771
|
+
];
|
|
1764
1772
|
var AMPLESS_PACKAGES = /* @__PURE__ */ new Set([
|
|
1765
1773
|
"ampless",
|
|
1766
1774
|
"@ampless/admin",
|
|
@@ -1882,6 +1890,63 @@ async function walkDir(dir, base, out) {
|
|
|
1882
1890
|
}
|
|
1883
1891
|
}
|
|
1884
1892
|
}
|
|
1893
|
+
async function listFilesRecursive(root) {
|
|
1894
|
+
const out = [];
|
|
1895
|
+
async function walk(current, relPrefix) {
|
|
1896
|
+
const entries = await readdir3(current, { withFileTypes: true });
|
|
1897
|
+
for (const entry of entries) {
|
|
1898
|
+
const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
|
|
1899
|
+
if (entry.isDirectory()) {
|
|
1900
|
+
await walk(join3(current, entry.name), rel);
|
|
1901
|
+
} else if (entry.isFile()) {
|
|
1902
|
+
out.push(rel);
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
if (!existsSync5(root)) return out;
|
|
1907
|
+
await walk(root, "");
|
|
1908
|
+
return out;
|
|
1909
|
+
}
|
|
1910
|
+
async function pruneEmptyDirs(root) {
|
|
1911
|
+
if (!existsSync5(root)) return;
|
|
1912
|
+
const entries = await readdir3(root, { withFileTypes: true });
|
|
1913
|
+
for (const entry of entries) {
|
|
1914
|
+
if (entry.isDirectory()) {
|
|
1915
|
+
await pruneEmptyDirs(join3(root, entry.name));
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
const remaining = await readdir3(root);
|
|
1919
|
+
if (remaining.length === 0) {
|
|
1920
|
+
await rm2(root, { recursive: true, force: true });
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
async function findObsoleteAppFiles(destDir, sharedDir) {
|
|
1924
|
+
const obsolete = [];
|
|
1925
|
+
for (const managedPath of AMPLESS_MANAGED_APP_PATHS) {
|
|
1926
|
+
const userPath = join3(destDir, managedPath);
|
|
1927
|
+
if (!existsSync5(userPath)) continue;
|
|
1928
|
+
const templatePath2 = join3(sharedDir, managedPath);
|
|
1929
|
+
const templateFiles = new Set(await listFilesRecursive(templatePath2));
|
|
1930
|
+
const userFiles = await listFilesRecursive(userPath);
|
|
1931
|
+
for (const f of userFiles) {
|
|
1932
|
+
if (!templateFiles.has(f)) {
|
|
1933
|
+
obsolete.push(`${managedPath}/${f}`);
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
return obsolete;
|
|
1938
|
+
}
|
|
1939
|
+
async function removeObsoleteAppFiles(destDir, paths) {
|
|
1940
|
+
for (const rel of paths) {
|
|
1941
|
+
const abs = join3(destDir, rel);
|
|
1942
|
+
if (existsSync5(abs)) {
|
|
1943
|
+
await rm2(abs, { force: true });
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
for (const managedPath of AMPLESS_MANAGED_APP_PATHS) {
|
|
1947
|
+
await pruneEmptyDirs(join3(destDir, managedPath));
|
|
1948
|
+
}
|
|
1949
|
+
}
|
|
1885
1950
|
function substituteVars(content, vars) {
|
|
1886
1951
|
return content.replace(/\{\{(\w+)\}\}/g, (_, key) => vars[key] ?? `{{${key}}}`);
|
|
1887
1952
|
}
|
|
@@ -1935,6 +2000,7 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
1935
2000
|
const shippedThemes = themeSyncEnabled ? await listShippedThemes(templatesRoot) : [];
|
|
1936
2001
|
const existingThemes = themeSyncEnabled ? await discoverInstalledThemes(destDir) : [];
|
|
1937
2002
|
const preservedThemes = existingThemes.filter((t) => !shippedThemes.includes(t));
|
|
2003
|
+
const obsoleteFiles = await findObsoleteAppFiles(destDir, sharedDir);
|
|
1938
2004
|
log3.info(
|
|
1939
2005
|
`replace: ${pc3.green(`\u8FFD\u52A0 ${replaceNew.length}`)} / ${pc3.yellow(`\u66F4\u65B0 ${replaceUpdate.length}`)}`
|
|
1940
2006
|
);
|
|
@@ -1949,6 +2015,9 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
1949
2015
|
`themes: ${pc3.cyan(`\u30C7\u30D5\u30A9\u30EB\u30C8\u30C6\u30FC\u30DE\u3092\u540C\u671F: ${shippedThemes.length}`)} / ${pc3.dim(`\u30AB\u30B9\u30BF\u30E0\u30C6\u30FC\u30DE (my-*) \u3092\u4FDD\u6301: ${preservedThemes.length}`)}`
|
|
1950
2016
|
);
|
|
1951
2017
|
}
|
|
2018
|
+
if (obsoleteFiles.length > 0) {
|
|
2019
|
+
log3.info(`cleanup: ${pc3.yellow(`\u524A\u9664 ${obsoleteFiles.length}`)} (ampless-managed app/ \u914D\u4E0B\u3067\u30C6\u30F3\u30D7\u30EC\u306B\u7121\u3044\u30D5\u30A1\u30A4\u30EB)`);
|
|
2020
|
+
}
|
|
1952
2021
|
log3.info(`protected: ${pc3.dim(`\u30C6\u30F3\u30D7\u30EC\u306B\u5B58\u5728\u3059\u308B\u304C\u89E6\u3089\u306A\u3044: ${classification.protected.length} \u500B`)}`);
|
|
1953
2022
|
if (opts.dryRun) {
|
|
1954
2023
|
return {
|
|
@@ -1958,7 +2027,8 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
1958
2027
|
protected: classification.protected,
|
|
1959
2028
|
themesSynced: shippedThemes,
|
|
1960
2029
|
themesPreserved: preservedThemes,
|
|
1961
|
-
packageJsonMerged: false
|
|
2030
|
+
packageJsonMerged: false,
|
|
2031
|
+
obsoleteRemoved: obsoleteFiles
|
|
1962
2032
|
};
|
|
1963
2033
|
}
|
|
1964
2034
|
for (const rel of classification.replace) {
|
|
@@ -1972,6 +2042,7 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
1972
2042
|
await copyWithSubstitution(src, dst, vars);
|
|
1973
2043
|
}
|
|
1974
2044
|
const themeResult = themeSyncEnabled ? await syncThemes(destDir, templatesRoot) : { synced: [], preserved: [] };
|
|
2045
|
+
await removeObsoleteAppFiles(destDir, obsoleteFiles);
|
|
1975
2046
|
const templatePkgRaw = await readFile3(join3(sharedDir, "package.json"), "utf-8");
|
|
1976
2047
|
const templatePkg = JSON.parse(templatePkgRaw);
|
|
1977
2048
|
const indent = detectIndent(projectPkgRaw);
|
|
@@ -2011,7 +2082,8 @@ async function runUpgradeIn(destDir, sharedDir, opts = {}) {
|
|
|
2011
2082
|
protected: classification.protected,
|
|
2012
2083
|
themesSynced: themeResult.synced,
|
|
2013
2084
|
themesPreserved: themeResult.preserved,
|
|
2014
|
-
packageJsonMerged: true
|
|
2085
|
+
packageJsonMerged: true,
|
|
2086
|
+
obsoleteRemoved: obsoleteFiles
|
|
2015
2087
|
};
|
|
2016
2088
|
}
|
|
2017
2089
|
async function runUpgrade(args) {
|
|
@@ -60,9 +60,7 @@
|
|
|
60
60
|
|
|
61
61
|
エージェントが投稿コンテンツを直接クエリ・編集できる。公開ツール: `list_posts`、`get_post`、`create_post`、`update_post`、`delete_post`、`upload_media`、`get_schema`。
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
**ローカル stdio(Claude Code / Cursor 向け、推奨):**
|
|
63
|
+
登録方法:
|
|
66
64
|
|
|
67
65
|
プロジェクトルートの `.mcp.json` に追加する:
|
|
68
66
|
```json
|
|
@@ -80,8 +78,6 @@
|
|
|
80
78
|
}
|
|
81
79
|
```
|
|
82
80
|
|
|
83
|
-
**管理者発行トークンによる HTTP**(ホスト型エージェント向け): `/admin/mcp-tokens` で Bearer トークンを発行し、エージェントクライアントを公開 MCP HTTP エンドポイントに向ける。エンドポイント URL は管理 UI で確認できる。
|
|
84
|
-
|
|
85
81
|
投稿本文は `markdown`、`html`、`tiptap`(JSON ドキュメント)の 3 フォーマットに対応している。
|
|
86
82
|
|
|
87
83
|
## 動作確認の基準
|
|
@@ -60,9 +60,7 @@ This file is written for AI coding agents (Claude Code, Cursor, Codex, etc.). Fo
|
|
|
60
60
|
|
|
61
61
|
Lets agents query and modify post content directly. Tools exposed: `list_posts`, `get_post`, `create_post`, `update_post`, `delete_post`, `upload_media`, `get_schema`.
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
**Local stdio (recommended for Claude Code / Cursor):**
|
|
63
|
+
Registration:
|
|
66
64
|
|
|
67
65
|
Add to `.mcp.json` at the project root:
|
|
68
66
|
```json
|
|
@@ -80,8 +78,6 @@ Add to `.mcp.json` at the project root:
|
|
|
80
78
|
}
|
|
81
79
|
```
|
|
82
80
|
|
|
83
|
-
**HTTP via admin-issued token** (for hosted agents): issue a Bearer token at `/admin/mcp-tokens` and point the agent client at the public MCP HTTP endpoint. See the admin UI for the exact endpoint URL.
|
|
84
|
-
|
|
85
81
|
Post bodies accept three formats: `markdown`, `html`, or `tiptap` (JSON document).
|
|
86
82
|
|
|
87
83
|
## Verification expectations
|
|
@@ -137,8 +137,6 @@ export default defineConfig({
|
|
|
137
137
|
| 変数 | 利用箇所 |
|
|
138
138
|
| --- | --- |
|
|
139
139
|
| `WEBHOOK_SECRET` | `@ampless/plugin-webhook` の HMAC 署名 |
|
|
140
|
-
| `AMPLESS_MCP_SERVICE_EMAIL` | HTTP MCP サービスユーザーのメール — `/api/mcp` を有効化するのに必須 |
|
|
141
|
-
| `AMPLESS_MCP_SERVICE_PASSWORD` | HTTP MCP サービスユーザーのパスワード |
|
|
142
140
|
|
|
143
141
|
env 変数追加 / 変更後は再デプロイをトリガーしてください。
|
|
144
142
|
|
|
@@ -157,7 +155,6 @@ ampless は 1 つの Amplify Hosting デプロイで複数のサイトを配信
|
|
|
157
155
|
ampless は MCP(Model Context Protocol)サーバーを同梱しているので、Claude Desktop / Cursor / Claude Code など MCP に対応した AI クライアントから投稿の読み書きができます。
|
|
158
156
|
|
|
159
157
|
- **ローカル / sandbox** — グローバルに 1 度入れる: `npx -y @ampless/mcp-server@alpha` に `amplify_outputs.json` のパスを渡す
|
|
160
|
-
- **本番(HTTP)** — `/admin/mcp-tokens` で Bearer トークンを発行し、MCP クライアントを `https://<your-domain>/api/mcp` に向ける。フルセットアップ: [docs/mcp-http-setup.ja.md](https://github.com/heavymoons/ampless/blob/main/docs/mcp-http-setup.ja.md) ([English](https://github.com/heavymoons/ampless/blob/main/docs/mcp-http-setup.md))
|
|
161
158
|
|
|
162
159
|
## ampless の更新
|
|
163
160
|
|
|
@@ -137,8 +137,6 @@ Set per-environment values in **Amplify Hosting console → Hosting → Environm
|
|
|
137
137
|
| Variable | Used by |
|
|
138
138
|
| --- | --- |
|
|
139
139
|
| `WEBHOOK_SECRET` | `@ampless/plugin-webhook` HMAC signing |
|
|
140
|
-
| `AMPLESS_MCP_SERVICE_EMAIL` | HTTP MCP service-user email — required to enable `/api/mcp` |
|
|
141
|
-
| `AMPLESS_MCP_SERVICE_PASSWORD` | HTTP MCP service-user password |
|
|
142
140
|
|
|
143
141
|
Trigger a redeploy after adding/changing env vars.
|
|
144
142
|
|
|
@@ -157,7 +155,6 @@ There's a caching trade-off in multi-site mode; details in [RUNBOOK.md → Multi
|
|
|
157
155
|
ampless ships an MCP (Model Context Protocol) server so Claude Desktop / Cursor / Claude Code / anything that speaks MCP can read and write your posts.
|
|
158
156
|
|
|
159
157
|
- **Local / sandbox** — install once globally: `npx -y @ampless/mcp-server@alpha` with the path to your `amplify_outputs.json`.
|
|
160
|
-
- **Production (HTTP)** — issue a Bearer token at `/admin/mcp-tokens` and point your MCP client at `https://<your-domain>/api/mcp`. Full setup: [docs/mcp-http-setup.md](https://github.com/heavymoons/ampless/blob/main/docs/mcp-http-setup.md) ([日本語](https://github.com/heavymoons/ampless/blob/main/docs/mcp-http-setup.ja.md)).
|
|
161
158
|
|
|
162
159
|
## Updating ampless
|
|
163
160
|
|
|
@@ -8,6 +8,7 @@ import { eventDispatcher } from './events/dispatcher/resource.js'
|
|
|
8
8
|
import { processorTrusted } from './events/processor-trusted/resource.js'
|
|
9
9
|
import { processorUntrusted } from './events/processor-untrusted/resource.js'
|
|
10
10
|
import { apiKeyRenewer } from './functions/api-key-renewer/resource.js'
|
|
11
|
+
import { mcpHandler } from './functions/mcp-handler/resource.js'
|
|
11
12
|
import { userAdmin } from './functions/user-admin/resource.js'
|
|
12
13
|
import { customizeBackend } from './backend.custom.js'
|
|
13
14
|
|
|
@@ -27,6 +28,7 @@ const backend = defineAmplessBackend({
|
|
|
27
28
|
processorTrusted,
|
|
28
29
|
processorUntrusted,
|
|
29
30
|
apiKeyRenewer,
|
|
31
|
+
mcpHandler,
|
|
30
32
|
userAdmin,
|
|
31
33
|
})
|
|
32
34
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { handler } from '@ampless/backend/functions/mcp-handler'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { defineFunction } from '@aws-amplify/backend'
|
|
2
|
+
|
|
3
|
+
export const mcpHandler = defineFunction({
|
|
4
|
+
name: 'mcp-handler',
|
|
5
|
+
entry: './handler.ts',
|
|
6
|
+
// Co-locate with the data stack so the function has the KvStore
|
|
7
|
+
// table ARN available without cross-stack references at synth time.
|
|
8
|
+
resourceGroupName: 'data',
|
|
9
|
+
memoryMB: 512,
|
|
10
|
+
timeoutSeconds: 30,
|
|
11
|
+
})
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"@ampless/plugin-rss": "^0.2.0-alpha.6",
|
|
27
27
|
"@ampless/plugin-seo": "^0.2.0-alpha.6",
|
|
28
28
|
"@ampless/plugin-webhook": "^0.2.0-alpha.6",
|
|
29
|
-
"@ampless/admin": "^0.2.0-alpha.
|
|
30
|
-
"@ampless/backend": "^0.2.0-alpha.
|
|
29
|
+
"@ampless/admin": "^0.2.0-alpha.19",
|
|
30
|
+
"@ampless/backend": "^0.2.0-alpha.9",
|
|
31
31
|
"@ampless/runtime": "^0.2.0-alpha.9",
|
|
32
32
|
"@digital-go-jp/tailwind-theme-plugin": "^0.3.4",
|
|
33
33
|
"ampless": "^0.2.0-alpha.6",
|
package/package.json
CHANGED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { admin } from '@/lib/admin'
|
|
2
|
-
import { createMcpRoute } from '@ampless/admin/api'
|
|
3
|
-
|
|
4
|
-
// `force-dynamic` keeps CloudFront from caching MCP responses. Each
|
|
5
|
-
// tool call must hit the SSR Lambda — caching would serve stale or
|
|
6
|
-
// cross-token responses.
|
|
7
|
-
export const dynamic = 'force-dynamic'
|
|
8
|
-
export const runtime = 'nodejs'
|
|
9
|
-
export const { POST } = createMcpRoute(admin)
|