@neopress/mcp 0.1.0

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.
@@ -0,0 +1,55 @@
1
+ # Neopress MCP Integration Drafts
2
+
3
+ These files prepare Neopress MCP for official client setup without submitting it
4
+ to any external directory yet.
5
+
6
+ ## Surfaces
7
+
8
+ | Client | Preferred transport | Draft artifact |
9
+ | --- | --- | --- |
10
+ | Codex CLI / IDE | Stdio locally, Streamable HTTP remotely | `codex/config.example.toml` |
11
+ | Claude Code | Stdio locally, Streamable HTTP remotely | `claude-code/setup.md` |
12
+ | Claude Desktop | MCPB local bundle | `claude-desktop/manifest.json` |
13
+ | Claude.ai | Remote MCP connector | `claude-remote/connector-draft.md` |
14
+ | ChatGPT | Remote MCP app in Developer Mode | `chatgpt/app-draft.md` |
15
+
16
+ ## Current Productization State
17
+
18
+ - Local stdio MCP is ready for agents that can launch `neopress-mcp`.
19
+ - Remote Streamable HTTP MCP is ready as a standalone server, pending deployment
20
+ to a stable public HTTPS origin such as `https://mcp.neopress.ai/mcp`.
21
+ - Production remote MCP requires a stable `NEOPRESS_MCP_OAUTH_CLIENT_SECRET` so
22
+ dynamically registered OAuth clients survive restarts and revisions.
23
+ - Tool descriptors include required read/write/destructive/open-world
24
+ annotations and short invocation status metadata for ChatGPT-compatible hosts.
25
+ - Remote OAuth metadata advertises refresh-token continuity through
26
+ `offline_access` and supports dynamic public client registration.
27
+ - Claude Desktop MCPB staging is prepared through
28
+ `pnpm --filter @neopress/mcp mcpb:stage`.
29
+
30
+ ## Not Ready For Submission Yet
31
+
32
+ - Verify final public MCP origin, CORS origins, and Cloud Run/service health.
33
+ - Verify final privacy policy, terms, support, and security contact URLs.
34
+ - Add production icon and screenshots for directories that request them.
35
+ - Prepare review/test credentials in a non-production Neopress workspace.
36
+ - Run real client scans in ChatGPT Developer Mode and Claude remote connector
37
+ settings after deployment.
38
+ - Decide whether remote active site selection should stay in-memory or move to
39
+ a durable store. Until then, clients should pass `siteId` explicitly or call
40
+ `neopress_site_select` at the beginning of a session.
41
+
42
+ ## Official References Checked
43
+
44
+ - OpenAI Apps SDK reference:
45
+ `https://developers.openai.com/apps-sdk/reference`
46
+ - OpenAI Developer Mode / MCP apps:
47
+ `https://help.openai.com/en/articles/12584461-developer-mode-and-mcp-apps-in-chatgpt`
48
+ - OpenAI Codex MCP:
49
+ `https://developers.openai.com/codex/mcp`
50
+ - Claude MCPB:
51
+ `https://claude.com/docs/connectors/building/mcpb`
52
+ - Claude Code MCP:
53
+ `https://docs.anthropic.com/en/docs/claude-code/mcp`
54
+ - Claude remote custom integrations:
55
+ `https://support.anthropic.com/en/articles/11175166-getting-started-with-custom-integrations-using-remote-mcp`
@@ -0,0 +1,96 @@
1
+ # ChatGPT MCP App Draft
2
+
3
+ Submission state: draft only. This is prepared for Developer Mode testing and
4
+ workspace review, not public submission.
5
+
6
+ ## App Metadata
7
+
8
+ | Field | Draft value |
9
+ | --- | --- |
10
+ | App name | Neopress |
11
+ | Developer / publisher | Neopress |
12
+ | Category | Productivity / Websites / CMS |
13
+ | MCP endpoint | `https://mcp.neopress.ai/mcp` |
14
+ | Authentication | OAuth |
15
+ | Website | `https://app.neopress.ai` |
16
+ | Privacy policy | TODO |
17
+ | Terms | TODO |
18
+ | Support | TODO |
19
+
20
+ ## Short Description
21
+
22
+ Create and publish Neopress websites from ChatGPT through OAuth-authenticated
23
+ MCP tools.
24
+
25
+ ## Long Description
26
+
27
+ Neopress lets teams create production websites with AI while keeping strict
28
+ contracts for layout TSX, page TSX, collections, entries, references, forms,
29
+ draft/prod state, edit history, cache invalidation, and publishing. The
30
+ Neopress MCP app gives ChatGPT a safe first-party tool surface for managing
31
+ that workflow without public API keys.
32
+
33
+ Users authenticate with Neopress OAuth. ChatGPT can list and select sites, read
34
+ the runtime contract, create and update pages, collections, entries, references,
35
+ forms, and assets, then publish entries and sites after explicit confirmation.
36
+
37
+ ## OAuth Configuration
38
+
39
+ - Authorization server: `https://mcp.neopress.ai`
40
+ - Protected resource: `https://mcp.neopress.ai/mcp`
41
+ - Authorization endpoint: `https://mcp.neopress.ai/oauth/authorize`
42
+ - Token endpoint: `https://mcp.neopress.ai/oauth/token`
43
+ - Dynamic registration endpoint: `https://mcp.neopress.ai/oauth/register`
44
+ - Scopes: `neopress:read`, `neopress:write`, `offline_access`
45
+ - PKCE: required, S256 only
46
+ - Token endpoint auth method: `none`
47
+ - Refresh grant: supported
48
+
49
+ ## Tool Categories
50
+
51
+ - Sites: list, select, get, create, update.
52
+ - Layout: get/update draft layout, global CSS, and 404.
53
+ - Pages: list, get, create, update, compile, delete.
54
+ - Collections: list, get, create, update schema, delete.
55
+ - Entries: list, get, create, update, publish, unpublish, delete, versions.
56
+ - References: list, create, delete entry reference edges.
57
+ - Forms: list, get, create, update, publish, delete, responses.
58
+ - Assets: list, get, register public URL, delete.
59
+ - Publish: preview and publish site draft state.
60
+
61
+ ## Safety And Constraints
62
+
63
+ - No public API keys. The API boundary is first-party OAuth only.
64
+ - The remote MCP server cannot upload files from the user's local filesystem.
65
+ - Generated pages cannot use visitor login, accounts, carts, checkout,
66
+ payments, subscriptions, or order management.
67
+ - Generated TSX must stay within Neopress allowed packages and runtime props.
68
+ - Dynamic pages must use numeric `collectionId` and `draftMeta.queryConfig`.
69
+ - Entry data keys must match collection schema field IDs.
70
+ - References must be stored through `entry_references`, then the source entry
71
+ must be republished.
72
+ - Forms must use `NeopressForm` and `formClient`; no manual `fetch`/`FormData`
73
+ submissions from generated pages.
74
+ - Publishing and destructive tools are annotated so ChatGPT can request user
75
+ confirmation.
76
+
77
+ ## Developer Mode Test Plan
78
+
79
+ 1. Add the MCP endpoint and choose OAuth.
80
+ 2. Scan tools and complete OAuth.
81
+ 3. Confirm read tools scan with read-only annotations.
82
+ 4. Confirm write tools show confirmation prompts where expected.
83
+ 5. Run the runtime contract prompt before generating any TSX.
84
+ 6. Create a test site, layout, collection, entry, dynamic page, form page, and
85
+ asset registration.
86
+ 7. Publish entry, form, and site.
87
+ 8. Fetch the live page URLs outside ChatGPT and confirm 200 responses.
88
+ 9. Reopen the app after token expiry and confirm refresh-token continuity.
89
+
90
+ ## Submission Blockers
91
+
92
+ - Deploy and verify `https://mcp.neopress.ai/mcp`.
93
+ - Fill legal/support URLs.
94
+ - Add a production icon and screenshots if requested by the review flow.
95
+ - Prepare review credentials and a throwaway Neopress workspace.
96
+ - Run the full Developer Mode scan once against the production endpoint.
@@ -0,0 +1,32 @@
1
+ # Claude Code Setup Draft
2
+
3
+ Use local stdio for development or private workstations:
4
+
5
+ ```bash
6
+ claude mcp add neopress --transport stdio -- neopress-mcp
7
+ ```
8
+
9
+ The local server reuses the CLI OAuth session from `neopress login`, or an
10
+ injected `NEOPRESS_ACCESS_TOKEN`.
11
+
12
+ Use remote Streamable HTTP after `https://mcp.neopress.ai/mcp` is deployed:
13
+
14
+ ```bash
15
+ claude mcp add neopress --transport http https://mcp.neopress.ai/mcp
16
+ ```
17
+
18
+ Then run `/mcp` inside Claude Code and complete the OAuth flow.
19
+
20
+ ## Smoke Prompts
21
+
22
+ - "List my Neopress sites and select site 725."
23
+ - "Read the Neopress runtime contract before making any page changes."
24
+ - "Preview pending publish changes for the active site."
25
+ - "Create a draft test page, compile it, publish the site, and give me the live URL."
26
+
27
+ ## Notes
28
+
29
+ - Do not expose public API keys. Neopress MCP uses first-party OAuth sessions.
30
+ - For remote MCP, local file upload is intentionally disabled. Use
31
+ `neopress_asset_register` with a public URL, or local stdio MCP for file
32
+ uploads from the workstation.
@@ -0,0 +1,100 @@
1
+ {
2
+ "manifest_version": "0.3",
3
+ "name": "neopress",
4
+ "display_name": "Neopress",
5
+ "version": "0.1.0",
6
+ "description": "Manage Neopress sites through a local MCP bridge that uses your Neopress OAuth session.",
7
+ "long_description": "Create and update Neopress sites, layout drafts, pages, collections, entries, forms, assets, and publish flows from Claude Desktop. This local MCPB bundle reuses the OAuth session created by the Neopress CLI and does not use API keys. Run `neopress login` before installing, or provide a short-lived OAuth access token for testing.",
8
+ "author": {
9
+ "name": "Neopress",
10
+ "url": "https://app.neopress.ai"
11
+ },
12
+ "homepage": "https://app.neopress.ai",
13
+ "documentation": "https://app.neopress.ai",
14
+ "server": {
15
+ "type": "node",
16
+ "entry_point": "server/bin.cjs",
17
+ "mcp_config": {
18
+ "command": "node",
19
+ "args": ["${__dirname}/server/bin.cjs"],
20
+ "env": {
21
+ "NEOPRESS_BASE_URL": "${user_config.base_url}",
22
+ "NEOPRESS_SITE_ID": "${user_config.site_id}",
23
+ "NEOPRESS_ACCESS_TOKEN": "${user_config.oauth_access_token}"
24
+ }
25
+ }
26
+ },
27
+ "tools": [
28
+ {
29
+ "name": "neopress_get_runtime_contract",
30
+ "description": "Read the required Neopress runtime contract before generating TSX, schemas, dynamic pages, references, forms, or publish flows."
31
+ },
32
+ {
33
+ "name": "neopress_sites_list",
34
+ "description": "List sites accessible to the current OAuth user."
35
+ },
36
+ {
37
+ "name": "neopress_site_select",
38
+ "description": "Verify access to a site and store it as the active site."
39
+ },
40
+ {
41
+ "name": "neopress_layout_update",
42
+ "description": "Update draft layout TSX, global CSS, or 404 TSX."
43
+ },
44
+ {
45
+ "name": "neopress_page_create",
46
+ "description": "Create a static or dynamic page draft."
47
+ },
48
+ {
49
+ "name": "neopress_collection_create",
50
+ "description": "Create a collection with a draft schema."
51
+ },
52
+ {
53
+ "name": "neopress_entry_create",
54
+ "description": "Create a draft entry whose data matches the collection schema."
55
+ },
56
+ {
57
+ "name": "neopress_form_create",
58
+ "description": "Create a dashboard-managed form."
59
+ },
60
+ {
61
+ "name": "neopress_asset_upload_file",
62
+ "description": "Upload a local file through a presigned Neopress asset URL."
63
+ },
64
+ {
65
+ "name": "neopress_publish_site",
66
+ "description": "Publish layout, pages, collections, and forms from draft to production."
67
+ }
68
+ ],
69
+ "tools_generated": true,
70
+ "keywords": ["neopress", "website", "cms", "publishing", "mcp"],
71
+ "compatibility": {
72
+ "claude_desktop": ">=1.0.0",
73
+ "platforms": ["darwin", "win32"],
74
+ "runtimes": {
75
+ "node": ">=18.0.0"
76
+ }
77
+ },
78
+ "user_config": {
79
+ "base_url": {
80
+ "type": "string",
81
+ "title": "Neopress base URL",
82
+ "description": "Neopress app/API origin.",
83
+ "default": "https://app.neopress.ai",
84
+ "required": true
85
+ },
86
+ "site_id": {
87
+ "type": "string",
88
+ "title": "Default site ID",
89
+ "description": "Optional numeric site ID. Leave blank and select a site with the MCP tool if unsure.",
90
+ "required": false
91
+ },
92
+ "oauth_access_token": {
93
+ "type": "string",
94
+ "title": "OAuth access token",
95
+ "description": "Optional short-lived OAuth access token for testing. Prefer `neopress login` so the MCP server can refresh the session.",
96
+ "sensitive": true,
97
+ "required": false
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,73 @@
1
+ # Claude Remote Connector Draft
2
+
3
+ Submission state: draft only. Do not submit until the production MCP URL,
4
+ privacy/support URLs, and test workspace are finalized.
5
+
6
+ ## Listing Metadata
7
+
8
+ | Field | Draft value |
9
+ | --- | --- |
10
+ | Connector name | Neopress |
11
+ | Publisher | Neopress |
12
+ | Category | Productivity / Websites / CMS |
13
+ | Remote MCP URL | `https://mcp.neopress.ai/mcp` |
14
+ | Website | `https://app.neopress.ai` |
15
+ | Support URL | TODO |
16
+ | Privacy policy URL | TODO |
17
+ | Terms URL | TODO |
18
+
19
+ ## Short Description
20
+
21
+ Build and operate Neopress websites from Claude using OAuth-authenticated MCP
22
+ tools for sites, layouts, pages, collections, entries, forms, assets, and
23
+ publishing.
24
+
25
+ ## Long Description
26
+
27
+ Neopress is an AI-native website builder. The Neopress connector lets Claude
28
+ manage a user's Neopress workspace through the same first-party API used by the
29
+ CLI and dashboard. Claude can create and update layouts, page TSX, collection
30
+ schemas, entries, references, dashboard-managed forms, assets, and publish
31
+ flows while following the Neopress runtime contract.
32
+
33
+ The connector does not expose public API keys. Users authenticate with
34
+ Neopress OAuth. Authorization and tenant membership are enforced by the
35
+ Neopress API on every request.
36
+
37
+ ## Authentication
38
+
39
+ - Type: OAuth 2.1 compatible authorization code with PKCE.
40
+ - Registration: dynamic public client registration at `/oauth/register`.
41
+ - Authorization metadata: `/.well-known/oauth-authorization-server`.
42
+ - Resource metadata: `/.well-known/oauth-protected-resource/mcp`.
43
+ - Scopes: `neopress:read`, `neopress:write`, `offline_access`.
44
+ - Refresh: supported by `/oauth/token` with `grant_type=refresh_token`.
45
+
46
+ ## Tool Safety Summary
47
+
48
+ - Read-only tools list and inspect sites, layout, pages, collections, entries,
49
+ references, forms, assets, and publish previews.
50
+ - Write tools mutate draft site state, schemas, entries, forms, or assets.
51
+ - Publishing tools move content to production and should require explicit user
52
+ confirmation.
53
+ - Delete and unpublish tools are marked destructive.
54
+ - Remote MCP cannot read the user's local filesystem; local file upload is
55
+ disabled in the remote server.
56
+
57
+ ## Review Test Prompts
58
+
59
+ 1. "List my Neopress sites and select the test site."
60
+ 2. "Read the Neopress runtime contract."
61
+ 3. "Create a collection named Connector Articles with title, slug, summary, and body fields."
62
+ 4. "Create and publish one entry in that collection."
63
+ 5. "Create a dynamic page for the entry, compile it, publish the site, and show the live URL."
64
+ 6. "Create a simple dashboard-managed contact form, publish it, add it to a page, and publish the site."
65
+
66
+ ## Submission Blockers
67
+
68
+ - Deploy remote MCP to a stable HTTPS origin.
69
+ - Verify OAuth refresh behavior in Claude after a token expiry window.
70
+ - Replace TODO legal/support URLs.
71
+ - Prepare a non-production test site and test credentials for review.
72
+ - Capture any required screenshots or screen recordings.
73
+ - Decide whether to persist active site selection outside process memory.
@@ -0,0 +1,13 @@
1
+ # Copy the relevant block into ~/.codex/config.toml or a project .codex/config.toml.
2
+
3
+ # Local stdio MCP. Requires either `neopress login` or an injected
4
+ # NEOPRESS_ACCESS_TOKEN OAuth access token.
5
+ [mcp_servers.neopress]
6
+ command = "neopress-mcp"
7
+ env = { NEOPRESS_BASE_URL = "https://app.neopress.ai" }
8
+
9
+ # Remote Streamable HTTP MCP. Use this after the hosted server is deployed.
10
+ # Then authenticate with: codex mcp login neopress-remote
11
+ #
12
+ # [mcp_servers.neopress-remote]
13
+ # url = "https://mcp.neopress.ai/mcp"
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@neopress/mcp",
3
+ "version": "0.1.0",
4
+ "description": "Neopress MCP server for first-party AI agents",
5
+ "type": "module",
6
+ "bin": {
7
+ "neopress-mcp": "./dist/bin.cjs",
8
+ "neopress-mcp-http": "./dist/http-bin.cjs"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.js",
15
+ "types": "./dist/index.d.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "integrations",
21
+ "scripts",
22
+ "README.md"
23
+ ],
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "dependencies": {
28
+ "@modelcontextprotocol/sdk": "^1.25.2",
29
+ "zod": "^4.1.12"
30
+ },
31
+ "devDependencies": {
32
+ "@types/node": "^22.0.0",
33
+ "tsup": "^8.5.1",
34
+ "typescript": "^5.7.0",
35
+ "vitest": "^4.1.5",
36
+ "@neopress/sdk": "0.1.0"
37
+ },
38
+ "scripts": {
39
+ "build": "tsup",
40
+ "mcpb:stage": "node ./scripts/prepare-mcpb-package.mjs",
41
+ "start:http": "node ./dist/http-bin.cjs",
42
+ "lint": "tsc --noEmit",
43
+ "test": "vitest run"
44
+ }
45
+ }
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5
+ ROOT_DIR="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
6
+
7
+ PROJECT_ID="${PROJECT_ID:-inblog-ver-2}"
8
+ REGION="${REGION:-us-west1}"
9
+ REPOSITORY="${REPOSITORY:-neopress}"
10
+ SERVICE="${SERVICE:-neopress-mcp-server}"
11
+ IMAGE="${IMAGE:-${REGION}-docker.pkg.dev/${PROJECT_ID}/${REPOSITORY}/${SERVICE}:latest}"
12
+ BUILD_STRATEGY="${BUILD_STRATEGY:-cloud-build}"
13
+ CLOUD_BUILD_TIMEOUT="${CLOUD_BUILD_TIMEOUT:-30m}"
14
+ PUBLIC_URL="${NEOPRESS_MCP_PUBLIC_URL:-}"
15
+ BASE_URL="${NEOPRESS_BASE_URL:-https://app.neopress.ai}"
16
+ AUTH_MODE="${NEOPRESS_MCP_AUTH_MODE:-oauth}"
17
+ CORS_ORIGINS="${NEOPRESS_MCP_CORS_ORIGINS:-}"
18
+ OAUTH_PROVIDER="${NEOPRESS_MCP_OAUTH_PROVIDER:-google}"
19
+
20
+ require_command() {
21
+ if ! command -v "$1" >/dev/null 2>&1; then
22
+ echo "Missing required command: $1" >&2
23
+ exit 1
24
+ fi
25
+ }
26
+
27
+ ensure_gcp_services_enabled() {
28
+ require_command gcloud
29
+
30
+ local services=(
31
+ artifactregistry.googleapis.com
32
+ cloudbuild.googleapis.com
33
+ run.googleapis.com
34
+ )
35
+
36
+ echo "Ensuring required GCP APIs are enabled"
37
+ gcloud services enable "${services[@]}" --project "${PROJECT_ID}"
38
+
39
+ for service in "${services[@]}"; do
40
+ until gcloud services list \
41
+ --enabled \
42
+ --project "${PROJECT_ID}" \
43
+ --filter="config.name:${service}" \
44
+ --format="value(config.name)" | grep -qx "${service}"; do
45
+ echo "Waiting for ${service} to become available..."
46
+ sleep 2
47
+ done
48
+ done
49
+ }
50
+
51
+ build_with_cloud_build() {
52
+ require_command gcloud
53
+ ensure_gcp_services_enabled
54
+
55
+ local config_file
56
+ config_file="$(mktemp "${TMPDIR:-/tmp}/neopress-mcp-cloudbuild.XXXXXX.yaml")"
57
+ trap "rm -f '${config_file}'" EXIT
58
+
59
+ cat >"${config_file}" <<EOF
60
+ steps:
61
+ - name: gcr.io/cloud-builders/docker
62
+ env:
63
+ - DOCKER_BUILDKIT=1
64
+ args:
65
+ - build
66
+ - --platform
67
+ - linux/amd64
68
+ - -f
69
+ - packages/mcp/Dockerfile
70
+ - -t
71
+ - ${IMAGE}
72
+ - .
73
+
74
+ images:
75
+ - ${IMAGE}
76
+ EOF
77
+
78
+ echo "Building and pushing ${IMAGE} with Cloud Build"
79
+ gcloud builds submit . \
80
+ --project "${PROJECT_ID}" \
81
+ --config "${config_file}" \
82
+ --timeout "${CLOUD_BUILD_TIMEOUT}"
83
+ }
84
+
85
+ build_with_local_docker() {
86
+ require_command docker
87
+
88
+ echo "Building and pushing ${IMAGE} with local Docker"
89
+ docker buildx build \
90
+ --platform linux/amd64 \
91
+ --provenance=false \
92
+ --sbom=false \
93
+ -f packages/mcp/Dockerfile \
94
+ -t "${IMAGE}" \
95
+ --push \
96
+ .
97
+ }
98
+
99
+ cd "${ROOT_DIR}"
100
+
101
+ case "${BUILD_STRATEGY}" in
102
+ cloud-build | cloudbuild)
103
+ build_with_cloud_build
104
+ ;;
105
+ docker | local-docker)
106
+ build_with_local_docker
107
+ ;;
108
+ *)
109
+ echo "Unknown BUILD_STRATEGY: ${BUILD_STRATEGY}" >&2
110
+ echo "Use BUILD_STRATEGY=cloud-build or BUILD_STRATEGY=docker." >&2
111
+ exit 1
112
+ ;;
113
+ esac
114
+
115
+ echo "Deploying ${SERVICE} to Cloud Run (${PROJECT_ID}/${REGION})"
116
+ require_command gcloud
117
+ service_exists=false
118
+ if gcloud run services describe "${SERVICE}" \
119
+ --project "${PROJECT_ID}" \
120
+ --region "${REGION}" >/dev/null 2>&1; then
121
+ service_exists=true
122
+ fi
123
+
124
+ env_vars=(
125
+ "NODE_ENV=production"
126
+ "NEOPRESS_BASE_URL=${BASE_URL}"
127
+ "NEOPRESS_MCP_AUTH_MODE=${AUTH_MODE}"
128
+ "NEOPRESS_MCP_OAUTH_PROVIDER=${OAUTH_PROVIDER}"
129
+ )
130
+
131
+ if [[ -n "${NEOPRESS_MCP_OAUTH_CLIENT_SECRET:-}" ]]; then
132
+ env_vars+=("NEOPRESS_MCP_OAUTH_CLIENT_SECRET=${NEOPRESS_MCP_OAUTH_CLIENT_SECRET}")
133
+ elif [[ "${service_exists}" == false ]]; then
134
+ echo "NEOPRESS_MCP_OAUTH_CLIENT_SECRET is required for first production remote MCP deploy." >&2
135
+ echo "Use a stable random value, for example: openssl rand -base64 32" >&2
136
+ exit 1
137
+ fi
138
+
139
+ if [[ -n "${PUBLIC_URL}" ]]; then
140
+ env_vars+=("NEOPRESS_MCP_PUBLIC_URL=${PUBLIC_URL}")
141
+ fi
142
+
143
+ if [[ -n "${CORS_ORIGINS}" ]]; then
144
+ env_vars+=("NEOPRESS_MCP_CORS_ORIGINS=${CORS_ORIGINS}")
145
+ fi
146
+
147
+ if [[ -n "${NEOPRESS_SUPABASE_URL:-}" ]]; then
148
+ env_vars+=("NEOPRESS_SUPABASE_URL=${NEOPRESS_SUPABASE_URL}")
149
+ fi
150
+
151
+ if [[ -n "${NEOPRESS_SUPABASE_ANON_KEY:-}" ]]; then
152
+ env_vars+=("NEOPRESS_SUPABASE_ANON_KEY=${NEOPRESS_SUPABASE_ANON_KEY}")
153
+ fi
154
+
155
+ env_arg="^~^$(IFS=~; echo "${env_vars[*]}")"
156
+
157
+ if [[ "${service_exists}" == true ]]; then
158
+ gcloud run services update "${SERVICE}" \
159
+ --project "${PROJECT_ID}" \
160
+ --region "${REGION}" \
161
+ --image "${IMAGE}" \
162
+ --update-env-vars "${env_arg}"
163
+ else
164
+ gcloud run deploy "${SERVICE}" \
165
+ --project "${PROJECT_ID}" \
166
+ --region "${REGION}" \
167
+ --image "${IMAGE}" \
168
+ --allow-unauthenticated \
169
+ --port 8080 \
170
+ --memory 512Mi \
171
+ --cpu 1 \
172
+ --min-instances 0 \
173
+ --max-instances 10 \
174
+ --set-env-vars "${env_arg}"
175
+ fi
176
+
177
+ echo "Latest ready revision:"
178
+ gcloud run services describe "${SERVICE}" \
179
+ --project "${PROJECT_ID}" \
180
+ --region "${REGION}" \
181
+ --format="value(status.latestReadyRevisionName,status.url)"
182
+
183
+ echo "Deployment complete"
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ copyFileSync,
4
+ existsSync,
5
+ mkdirSync,
6
+ rmSync,
7
+ writeFileSync,
8
+ } from 'node:fs';
9
+ import { dirname, resolve } from 'node:path';
10
+ import { fileURLToPath } from 'node:url';
11
+
12
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
13
+ const distBin = resolve(packageRoot, 'dist/bin.cjs');
14
+ const manifest = resolve(packageRoot, 'integrations/claude-desktop/manifest.json');
15
+ const outDir = resolve(packageRoot, 'dist/mcpb/neopress');
16
+ const outServerDir = resolve(outDir, 'server');
17
+
18
+ if (!existsSync(distBin)) {
19
+ console.error('Missing dist/bin.cjs. Run `pnpm --filter @neopress/mcp build` first.');
20
+ process.exit(1);
21
+ }
22
+
23
+ if (!existsSync(manifest)) {
24
+ console.error('Missing Claude Desktop MCPB manifest.');
25
+ process.exit(1);
26
+ }
27
+
28
+ rmSync(outDir, { recursive: true, force: true });
29
+ mkdirSync(outServerDir, { recursive: true });
30
+
31
+ copyFileSync(manifest, resolve(outDir, 'manifest.json'));
32
+ copyFileSync(distBin, resolve(outServerDir, 'bin.cjs'));
33
+ writeFileSync(
34
+ resolve(outDir, 'README.md'),
35
+ [
36
+ '# Neopress MCPB Draft',
37
+ '',
38
+ 'This bundle stages the local Neopress stdio MCP server for Claude Desktop.',
39
+ '',
40
+ 'Before installing, run `neopress login` so the MCP server can reuse and refresh',
41
+ 'the first-party OAuth session. Alternatively, configure a short-lived OAuth',
42
+ 'access token in the extension settings for testing only.',
43
+ '',
44
+ 'To pack this directory:',
45
+ '',
46
+ '```bash',
47
+ 'pnpm dlx @anthropic-ai/mcpb pack packages/mcp/dist/mcpb/neopress',
48
+ '```',
49
+ '',
50
+ ].join('\n'),
51
+ 'utf-8'
52
+ );
53
+
54
+ console.log(`Staged MCPB directory: ${outDir}`);
55
+ console.log(`Pack with: pnpm dlx @anthropic-ai/mcpb pack ${outDir}`);