@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.
- package/README.md +116 -0
- package/dist/bin.cjs +32064 -0
- package/dist/http-bin.cjs +34058 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +33999 -0
- package/integrations/README.md +55 -0
- package/integrations/chatgpt/app-draft.md +96 -0
- package/integrations/claude-code/setup.md +32 -0
- package/integrations/claude-desktop/manifest.json +100 -0
- package/integrations/claude-remote/connector-draft.md +73 -0
- package/integrations/codex/config.example.toml +13 -0
- package/package.json +45 -0
- package/scripts/deploy-cloud-run.sh +183 -0
- package/scripts/prepare-mcpb-package.mjs +55 -0
|
@@ -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}`);
|