@dalcontak/blogger-mcp-server 1.0.1 → 1.0.3

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.
@@ -6,7 +6,7 @@ on:
6
6
  - 'release/**'
7
7
 
8
8
  permissions:
9
- contents: read
9
+ contents: write # Needed for creating GitHub Releases and Tags
10
10
  id-token: write
11
11
 
12
12
  jobs:
@@ -35,3 +35,32 @@ jobs:
35
35
  run: npm publish --provenance --access public
36
36
  env:
37
37
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
38
+
39
+ - name: Extract version from package.json
40
+ id: package-version
41
+ run: echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT
42
+
43
+ - name: Create GitHub Release
44
+ env:
45
+ GH_TOKEN: ${{ github.token }}
46
+ run: |
47
+ VERSION="v${{ steps.package-version.outputs.version }}"
48
+
49
+ # gh release create will automatically create the tag if it doesn't exist
50
+ # --generate-notes builds an automated changelog from commit history
51
+ gh release create "$VERSION" \
52
+ --title "Release $VERSION" \
53
+ --generate-notes \
54
+ --notes "### 📦 Installation
55
+ Available on npm: [npmjs.com/package/@dalcontak/blogger-mcp-server](https://www.npmjs.com/package/@dalcontak/blogger-mcp-server)
56
+
57
+ You can run the server directly without installing it using \`npx\`:
58
+ \`\`\`bash
59
+ npx -y @dalcontak/blogger-mcp-server
60
+ \`\`\`
61
+
62
+ Or install it globally:
63
+ \`\`\`bash
64
+ npm install -g @dalcontak/blogger-mcp-server
65
+ \`\`\`" \
66
+ --target $GITHUB_SHA
package/README.md CHANGED
@@ -37,48 +37,54 @@ npm run build
37
37
 
38
38
  ### Option 1: API Key (Read-only)
39
39
 
40
- Access public blogs only.
40
+ Access public blogs only. Useful if you only need to read data.
41
41
 
42
42
  1. Go to [Google Cloud Console](https://console.cloud.google.com/)
43
- 2. Create/select a project or existing one
44
- 3. Enable the **Blogger API v3**
45
- 4. Create an **API Key**
46
- 5. Set the environment variable:
43
+ 2. Create/select a project, then enable the **Blogger API v3**.
44
+ 3. Create an **API Key** under Credentials.
45
+ 4. Set the environment variable: `export BLOGGER_API_KEY=your_api_key_here`
47
46
 
48
- ```bash
49
- export BLOGGER_API_KEY=your_api_key_here
50
- ```
47
+ ### Option 2: OAuth2 (Full Access)
51
48
 
52
- Works for: `get_blog`, `get_blog_by_url`, `list_posts`, `get_post`, `search_posts`, `list_labels`, `get_label`
49
+ Required to create, update, delete posts, and list your own blogs.
53
50
 
54
- ### Option 2: OAuth2 (Full Access)
51
+ **Need a step-by-step visual guide?**
52
+ 🔗 [**Read the complete tutorial on setting up OAuth2 for Blogger MCP here**](https://dalcontk.blogspot.com/2026/03/guia-paso-paso-como-configurar.html)
53
+ *(Note: This guide is written in Spanish. Feel free to use Google Translate if you need it in another language).*
55
54
 
56
- Required for: `list_blogs`, `create_post`, `update_post`, `delete_post`
55
+ **Step 1: Configure OAuth Consent**
56
+ 1. In [Google Cloud Console](https://console.cloud.google.com/), go to **Google Auth Platform** > **Overview**.
57
+ 2. Under **Audience**, add your Google account as a Test User.
58
+ 3. Under **Data Access** (Scopes), add: `https://www.googleapis.com/auth/blogger`
57
59
 
58
- 1. Go to [Google Cloud Console](https://console.cloud.google.com/)
59
- 2. Navigate to **Credentials** > **Create Credentials**
60
- 3. Select **OAuth client ID**
61
- 4. Application type: **Web application** or **Desktop app**
62
- 5. Name: Your app name
63
- 6. Authorized redirect URI: `http://localhost` (or your actual redirect)
64
- 7. Scopes: Select **`https://www.googleapis.com/auth/blogger`**
65
- 8. Create credentials and note the **Client ID** and **Client Secret**
66
-
67
- To obtain a refresh token (one-time setup):
68
- - Use the [OAuth Playground](https://developers.google.com/oauthplayground/)
69
- - Select **Blogger API v3**
70
- - Choose `https://www.googleapis.com/auth/blogger` scope
71
- - Authorize and copy the **refresh token**
72
-
73
- Set environment variables:
60
+ **Step 2: Create Web Credentials**
61
+ 1. Go to **Credentials** > **Create Credentials** > **OAuth client ID**.
62
+ 2. Application type: Select **Web application** *(do not use Desktop app)*.
63
+ 3. Name: Your app name.
64
+ 4. Authorized redirect URIs: Add exactly `https://developers.google.com/oauthplayground`
65
+ 5. Click Create and copy your **Client ID** and **Client Secret**.
74
66
 
75
- ```bash
76
- export GOOGLE_CLIENT_ID=your_client_id
77
- export GOOGLE_CLIENT_SECRET=your_client_secret
78
- export GOOGLE_REFRESH_TOKEN=your_refresh_token
67
+ **Step 3: Get a Refresh Token**
68
+ 1. Go to the [Google OAuth 2.0 Playground](https://developers.google.com/oauthplayground/).
69
+ 2. Click the **Gear icon** (top right) ⚙️ > check **Use your own OAuth credentials**.
70
+ 3. Paste your **Client ID** and **Client Secret**, then close the settings panel.
71
+ 4. In Step 1 (left panel), scroll to **Blogger API v3**, select `https://www.googleapis.com/auth/blogger`, and click **Authorize APIs**.
72
+ 5. Log in with your test Google account and grant permissions.
73
+ 6. In Step 2, click **Exchange authorization code for tokens**.
74
+ 7. Copy the generated **Refresh token**.
75
+
76
+ **Step 4: Set Environment Variables**
77
+ Configure your MCP client (like Claude Desktop or OpenCode) with:
78
+
79
+ ```json
80
+ "env": {
81
+ "GOOGLE_CLIENT_ID": "your_client_id_here",
82
+ "GOOGLE_CLIENT_SECRET": "your_client_secret_here",
83
+ "GOOGLE_REFRESH_TOKEN": "1//your_refresh_token_here"
84
+ }
79
85
  ```
80
86
 
81
- > **Note:** If both authentication methods are configured, OAuth2 is used (it covers all operations).
87
+ > **Note:** If both API Key and OAuth2 are configured, OAuth2 is used.
82
88
 
83
89
  ## Usage
84
90
 
@@ -108,7 +114,7 @@ Create or edit your Claude Desktop config file:
108
114
  "mcpServers": {
109
115
  "blogger": {
110
116
  "command": "node",
111
- "args": ["/home/dalcon/dev/ai/blogger-mcp-server/dist/index.js"],
117
+ "args": ["./dist/index.js"],
112
118
  "env": {
113
119
  "BLOGGER_API_KEY": "your_api_key_here"
114
120
  }
@@ -197,7 +203,7 @@ src/
197
203
  config.ts # Environment-based configuration object
198
204
  types.ts # Shared interfaces and type definitions
199
205
  ui-manager.ts # Express + Socket.IO web dashboard
200
- tests/ # Unit tests (Jest)
206
+ *.test.ts # Unit tests (Jest) alongside source files
201
207
  .github/workflows/ # GitHub Actions CI/CD
202
208
  public/ # Static web UI assets (HTML/JS/CSS)
203
209
  dist/ # Compiled output
package/RELEASE.md CHANGED
@@ -4,28 +4,55 @@ This document explains how to create releases and publish to npm.
4
4
 
5
5
  ## Prerequisites
6
6
 
7
- ### Setup Trusted Publishing (OIDC)
7
+ ### Setup GitHub Actions Token + Trusted Publishing (Provenance)
8
8
 
9
- IMPORTANT: npm classic tokens are deprecated (revoked Nov 19, 2025). Fine-grained tokens have a 90-day maximum lifetime. The recommended approach is **Trusted Publishing (OIDC)**.
9
+ We use a **hybrid approach** combining a classic automation token for authentication with OIDC (OpenID Connect) for cryptographic provenance signing.
10
10
 
11
- #### Step 1: Configure GitHub Actions as Trusted Publisher
11
+ #### Step 1: Create Fine-grained Automation Token
12
+
13
+ 1. Log in to npm: `npm login`
14
+ 2. Generate an automation token (90-day lifetime):
15
+ ```bash
16
+ npm token create --name="github-actions" --scopes="dalcontak" --packages-and-scopes-permission="read-write" --bypass-2fa=true
17
+ ```
18
+ 3. Copy the token (starts with `npm_`)
19
+
20
+ #### Step 2: Add Token to GitHub Secrets
21
+
22
+ 1. Go to your repository: https://github.com/dalcontak/blogger-mcp-server
23
+ 2. Navigate to **Settings** > **Secrets and variables** > **Actions**
24
+ 3. Click **"New repository secret"**
25
+ 4. Name: `NPM_TOKEN`
26
+ 5. Value: Paste the token from Step 1
27
+ 6. Click **"Add secret"**
28
+
29
+ #### Step 3: Configure npm Package Settings for Automation Tokens
30
+
31
+ 1. Go to https://www.npmjs.com/package/@dalcontak/blogger-mcp-server
32
+ 2. Click on **"Settings"** tab
33
+ 3. In **Publishing access**, change to: **"Require two-factor authentication or a token"** (or similar option that allows automation tokens)
34
+ 4. Save the change
35
+
36
+ ⚠️ **Important:** If set to "Require 2FA" (without "or a token"), automation tokens will be rejected with error 403.
37
+
38
+ #### Step 4: Configure GitHub Actions as Trusted Publisher (for Provenance)
39
+
40
+ This enables cryptographic signing of published packages (npm provenance).
12
41
 
13
- The correct navigation is:
14
42
  1. Go to https://www.npmjs.com/package/@dalcontak/blogger-mcp-server
15
43
  2. Click on **"Settings"** tab
16
44
  3. Find the **"Trusted Publishers"** section
17
- 4. Click on **"GitHub Actions"** (or "Set up connection" button)
45
+ 4. Click on **"Set up connection"**
18
46
  5. Fill in:
19
- - **Organization or user**: `dalcontak`
20
- - **Repository**: `dalcontak/blogger-mcp-server`
21
- - **Workflow filename**: `publish.yml` (name of the workflow file, NOT the full path)
47
+ - **Organization or user**: `dalcontak`
48
+ - **Repository**: `dalcontak/blogger-mcp-server`
49
+ - **Workflow filename**: `publish.yml`
22
50
  6. Click **"Set up connection"**
23
51
 
24
- Optional but recommended:
25
- - **Require two-factor authentication**
26
- - **Disallow tokens** (forces OIDC usage)
27
-
28
- That's it! No token needed — OIDC generates short-lived, job-specific credentials automatically.
52
+ That's it! The workflow will now:
53
+ - Authenticate with your `NPM_TOKEN` secret
54
+ - Sign packages with GitHub Actions provenance
55
+ - Publish to npm securely
29
56
 
30
57
  ## Version Naming
31
58
 
@@ -76,7 +103,7 @@ GitHub Actions will automatically:
76
103
  3. ✅ Install dependencies
77
104
  4. ✅ Run tests (npm test)
78
105
  5. ✅ Build (npm run build)
79
- 6. ✅ Publish to npm using OIDC (no manual token needed)
106
+ 6. ✅ Publish to npm using `NPM_TOKEN` + provenance signing
80
107
 
81
108
  The workflow triggers on any push to branches matching `release/**`.
82
109
 
@@ -97,29 +124,34 @@ npm install @dalcontak/blogger-mcp-server
97
124
  - The branch name **must** contain version after "release-" (e.g., `release-1.1.7`)
98
125
  - Version format: **3 digits separated by dots** (X.Y.Z)
99
126
  - Only pushes to `release/**` branches trigger the build/publish workflow
100
- - **No GitHub secrets needed** for OIDC just configure trusted publisher connection
101
- - Requires Node.js >= 11.5.1 (npm automatically detects OIDC credentials)
127
+ - **NPM_TOKEN secret is required** in GitHub Actions for authentication (automation token)
128
+ - Token lifetime is 90 days maximum remember to recreate and update secret before expiration
129
+ - Requires Node.js >= 20 (used in workflow)
102
130
 
103
131
  ## Troubleshooting
104
132
 
105
- ### Error: "You are not set up to publish with OIDC"
133
+ ### Error: "Two-factor authentication is required but an automation token was specified"
134
+
135
+ **Solution:** Make sure npm package settings allow automation tokens:
136
+ 1. Go to https://www.npmjs.com/package/@dalcontak/blogger-mcp-server > Settings
137
+ 2. Change **Publishing access** to: "Require two-factor authentication or a token"
138
+ 3. Save and try the workflow again
139
+
140
+ ### Error: "E403: You cannot publish over previously published versions"
106
141
 
107
- Make sure:
108
- 1. GitHub Actions is configured as **Trusted Publisher** in npm settings
109
- 2. Workflow filename matches: `publish.yml`
110
- 3. Repository is correct: `dalcontak/blogger-mcp-server`
111
- 4. Workflow has `permissions: id-token: write`
142
+ **Solution:** This is normal. You cannot republish an existing version. Bump the version in `package.json` and create a new release branch.
112
143
 
113
- ### Error: "npm publish --provenance requires Node.js >= 11.5.1"
144
+ ### Error: "E404: Package not in this registry"
114
145
 
115
- The workflow uses `actions/setup-node@v6` which sets up Node.js 20. If you see this error, check your Node.js version.
146
+ **Solution:** Make sure `NPM_TOKEN` is set as a GitHub secret and that the token has write permissions for `@dalcontak` scope.
116
147
 
117
- ## Comparison: Tokens vs OIDC
148
+ ## Comparison: Token vs OIDC (Provenance)
118
149
 
119
- | Aspect | Classic Tokens | Fine-grained Tokens | Trusted Publishing (OIDC) |
120
- |---------|----------------|--------------------|---------------------------|
121
- | **Status** | Deprecated (revoked) | ⚠️ Limited to 90 days | Recommended |
122
- | **Rotation** | Manual (every 90 days max) | Manual (every 90 days max) | Automatic (job-specific) |
123
- | **Security** | Long-lived, stored in secrets | Short-lived, stored in secrets | Short-lived, job-specific |
124
- | **GitHub Secret** | Required (NPM_TOKEN) | Required (NPM_TOKEN) | Not needed |
125
- | **Setup** | Create token, add to secrets | Create token, add to secrets | Configure in npmjs.com |
150
+ | Aspect | Fine-grained Token | OIDC (Provenance) | Our Hybrid Approach |
151
+ |---------|--------------------|---------------------|-------------------|
152
+ | **Purpose** | Authentication (publish permission) | Cryptographic signing (supply chain security) | Both combined |
153
+ | **Status** | ⚠️ Limited to 90 days | Standard feature | Best practice |
154
+ | **Rotation** | Manual (recreate every 90 days) | Automatic (job-specific) | Token manual, OIDC automatic |
155
+ | **Security** | Stored in GitHub secrets | Job-specific, short-lived | Multi-layer security |
156
+ | **GitHub Secret** | Required (NPM_TOKEN) | Not needed | Required (NPM_TOKEN) |
157
+ | **Setup** | Create via CLI, add to secret | Configure in npmjs.com | Both steps required |
@@ -1,121 +1,28 @@
1
1
  import { blogger_v3 } from 'googleapis';
2
- import { BloggerBlog, BloggerPost, BloggerLabel } from './types';
3
- /**
4
- * Custom types to compensate for Blogger API limitations
5
- */
2
+ import { BloggerPost } from './types';
6
3
  interface BloggerLabelList {
7
4
  kind?: string;
8
- items?: BloggerLabel[];
5
+ items?: Array<{
6
+ name: string;
7
+ }>;
9
8
  }
10
- /**
11
- * Google Blogger API interaction service
12
- *
13
- * Supports two authentication modes:
14
- * - OAuth2 (GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET + GOOGLE_REFRESH_TOKEN):
15
- * full access (read + write). Required for listBlogs, createPost, updatePost, deletePost.
16
- * - API Key (BLOGGER_API_KEY): read-only access to public blogs.
17
- * Works for getBlog, listPosts, getPost, searchPosts, listLabels, getLabel.
18
- *
19
- * If both are configured, OAuth2 is used (it covers all operations).
20
- */
21
9
  export declare class BloggerService {
22
10
  private blogger;
23
11
  private readonly isOAuth2;
24
- /**
25
- * Initializes the Blogger service with OAuth2 or API key
26
- */
27
12
  constructor();
28
- /**
29
- * Checks that OAuth2 authentication is available.
30
- * Throws an explicit error if the operation requires OAuth2 and we are in API key mode.
31
- */
32
13
  private requireOAuth2;
33
- /**
34
- * Lists all blogs for the authenticated user.
35
- * Requires OAuth2 (blogs.listByUser with userId: 'self').
36
- * @returns Blog list
37
- */
38
14
  listBlogs(): Promise<blogger_v3.Schema$BlogList>;
39
- /**
40
- * Retrieves details of a specific blog
41
- * @param blogId ID of the blog to retrieve
42
- * @returns Blog details
43
- */
44
15
  getBlog(blogId: string): Promise<blogger_v3.Schema$Blog>;
45
- /**
46
- * Retrieves a blog by its URL
47
- * @param url Blog URL
48
- * @returns Blog details
49
- */
50
16
  getBlogByUrl(url: string): Promise<blogger_v3.Schema$Blog>;
51
- /**
52
- * Simulates blog creation.
53
- * Note: The Blogger API does not actually allow creating a blog via API.
54
- * This method simulates the functionality and returns an explanatory error message.
55
- *
56
- * @param blogData Blog data to create
57
- * @returns Explanatory error message
58
- */
59
- createBlog(blogData: Partial<BloggerBlog>): Promise<any>;
60
- /**
61
- * Lists posts from a blog
62
- * @param blogId Blog ID
63
- * @param maxResults Maximum number of results to return
64
- * @returns Post list
65
- */
66
17
  listPosts(blogId: string, maxResults?: number): Promise<blogger_v3.Schema$PostList>;
67
- /**
68
- * Searches posts in a blog using the native posts.search endpoint of the Blogger API
69
- * @param blogId Blog ID
70
- * @param query Search term
71
- * @param maxResults Maximum number of results to return
72
- * @returns List of matching posts
73
- */
74
18
  searchPosts(blogId: string, query: string, maxResults?: number): Promise<blogger_v3.Schema$PostList>;
75
- /**
76
- * Retrieves a specific post
77
- * @param blogId Blog ID
78
- * @param postId Post ID
79
- * @returns Post details
80
- */
81
19
  getPost(blogId: string, postId: string): Promise<blogger_v3.Schema$Post>;
82
- /**
83
- * Creates a new post in a blog.
84
- * Requires OAuth2.
85
- * @param blogId Blog ID
86
- * @param postData Post data to create
87
- * @returns Created post
88
- */
89
20
  createPost(blogId: string, postData: Partial<BloggerPost>): Promise<blogger_v3.Schema$Post>;
90
- /**
91
- * Updates an existing post.
92
- * Requires OAuth2.
93
- * @param blogId Blog ID
94
- * @param postId Post ID
95
- * @param postData Post data to update
96
- * @returns Updated post
97
- */
98
21
  updatePost(blogId: string, postId: string, postData: Partial<BloggerPost>): Promise<blogger_v3.Schema$Post>;
99
- /**
100
- * Deletes a post.
101
- * Requires OAuth2.
102
- * @param blogId Blog ID
103
- * @param postId Post ID
104
- * @returns Deletion status
105
- */
106
22
  deletePost(blogId: string, postId: string): Promise<void>;
107
- /**
108
- * Lists labels from a blog
109
- * @param blogId Blog ID
110
- * @returns Label list
111
- */
112
23
  listLabels(blogId: string): Promise<BloggerLabelList>;
113
- /**
114
- * Retrieves a specific label
115
- * @param blogId Blog ID
116
- * @param labelName Label name
117
- * @returns Label details
118
- */
119
- getLabel(blogId: string, labelName: string): Promise<BloggerLabel>;
24
+ getLabel(blogId: string, labelName: string): Promise<{
25
+ name: string;
26
+ }>;
120
27
  }
121
28
  export {};