facebook-mcp-server 1.6.6

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.
Files changed (64) hide show
  1. package/.env.example +2 -0
  2. package/.github/dependabot.yml +50 -0
  3. package/.github/workflows/ci.yml +51 -0
  4. package/.github/workflows/release.yml +200 -0
  5. package/CONTRIBUTING.md +112 -0
  6. package/LICENSE +21 -0
  7. package/README.md +128 -0
  8. package/dist/client.d.ts +57 -0
  9. package/dist/client.js +140 -0
  10. package/dist/client.test.d.ts +9 -0
  11. package/dist/client.test.js +211 -0
  12. package/dist/create-post.d.ts +39 -0
  13. package/dist/create-post.js +85 -0
  14. package/dist/create-post.test.d.ts +11 -0
  15. package/dist/create-post.test.js +175 -0
  16. package/dist/errors.d.ts +12 -0
  17. package/dist/errors.js +87 -0
  18. package/dist/errors.test.d.ts +9 -0
  19. package/dist/errors.test.js +162 -0
  20. package/dist/first-comment.test.d.ts +10 -0
  21. package/dist/first-comment.test.js +54 -0
  22. package/dist/handlers.test.d.ts +19 -0
  23. package/dist/handlers.test.js +333 -0
  24. package/dist/index.d.ts +44 -0
  25. package/dist/index.js +374 -0
  26. package/dist/lib/index.d.ts +9 -0
  27. package/dist/lib/index.js +8 -0
  28. package/dist/lib/insights.d.ts +53 -0
  29. package/dist/lib/insights.js +47 -0
  30. package/dist/rate-limiter.d.ts +71 -0
  31. package/dist/rate-limiter.js +214 -0
  32. package/dist/rate-limiter.test.d.ts +1 -0
  33. package/dist/rate-limiter.test.js +154 -0
  34. package/dist/response.d.ts +24 -0
  35. package/dist/response.js +35 -0
  36. package/dist/response.test.d.ts +1 -0
  37. package/dist/response.test.js +71 -0
  38. package/dist/sanitize.d.ts +17 -0
  39. package/dist/sanitize.js +27 -0
  40. package/dist/sanitize.test.d.ts +1 -0
  41. package/dist/sanitize.test.js +43 -0
  42. package/dist/tools.test.d.ts +16 -0
  43. package/dist/tools.test.js +150 -0
  44. package/package.json +29 -0
  45. package/src/client.test.ts +284 -0
  46. package/src/client.ts +204 -0
  47. package/src/create-post.test.ts +196 -0
  48. package/src/create-post.ts +118 -0
  49. package/src/errors.test.ts +297 -0
  50. package/src/errors.ts +108 -0
  51. package/src/first-comment.test.ts +73 -0
  52. package/src/handlers.test.ts +431 -0
  53. package/src/index.ts +540 -0
  54. package/src/lib/index.ts +9 -0
  55. package/src/lib/insights.ts +150 -0
  56. package/src/rate-limiter.test.ts +186 -0
  57. package/src/rate-limiter.ts +252 -0
  58. package/src/response.test.ts +80 -0
  59. package/src/response.ts +43 -0
  60. package/src/sanitize.test.ts +52 -0
  61. package/src/sanitize.ts +35 -0
  62. package/src/tools.test.ts +195 -0
  63. package/tsconfig.json +15 -0
  64. package/vitest.config.ts +10 -0
package/.env.example ADDED
@@ -0,0 +1,2 @@
1
+ FACEBOOK_ACCESS_TOKEN=your_long_lived_facebook_page_token_here
2
+ FACEBOOK_PAGE_ID=your_facebook_page_id_here
@@ -0,0 +1,50 @@
1
+ version: 2
2
+ updates:
3
+ # npm dependencies
4
+ - package-ecosystem: "npm"
5
+ directory: "/"
6
+ schedule:
7
+ interval: "weekly"
8
+ day: "monday"
9
+ open-pull-requests-limit: 5
10
+ labels:
11
+ - "dependencies"
12
+ commit-message:
13
+ prefix: "chore(deps)"
14
+ groups:
15
+ dev-dependencies:
16
+ patterns:
17
+ - "typescript"
18
+ - "tsx"
19
+ - "vitest"
20
+ - "@types/*"
21
+ - "prettier"
22
+ update-types:
23
+ - "minor"
24
+ - "patch"
25
+ runtime-dependencies:
26
+ patterns:
27
+ - "@modelcontextprotocol/*"
28
+ - "zod"
29
+ update-types:
30
+ - "patch"
31
+
32
+ # GitHub Actions
33
+ - package-ecosystem: "github-actions"
34
+ directory: "/"
35
+ schedule:
36
+ interval: "weekly"
37
+ day: "monday"
38
+ open-pull-requests-limit: 3
39
+ labels:
40
+ - "dependencies"
41
+ - "github-actions"
42
+ commit-message:
43
+ prefix: "chore(deps)"
44
+ groups:
45
+ github-actions:
46
+ patterns:
47
+ - "*"
48
+ update-types:
49
+ - "minor"
50
+ - "patch"
@@ -0,0 +1,51 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test-matrix:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ node-version: ["20", "22"]
15
+
16
+ steps:
17
+ - uses: actions/checkout@v7
18
+
19
+ - name: Set up Node.js ${{ matrix.node-version }}
20
+ uses: actions/setup-node@v6
21
+ with:
22
+ node-version: ${{ matrix.node-version }}
23
+ cache: "npm"
24
+
25
+ - name: Install dependencies
26
+ run: npm ci
27
+
28
+ - name: Format check with prettier
29
+ run: npx prettier --check .
30
+
31
+ - name: Type check with tsc
32
+ run: npx tsc --noEmit
33
+
34
+ - name: Run tests
35
+ run: npm test
36
+
37
+ - name: Build
38
+ run: npm run build
39
+
40
+ test:
41
+ needs: [test-matrix]
42
+ runs-on: ubuntu-latest
43
+ if: always()
44
+ steps:
45
+ - name: Check test matrix status
46
+ run: |
47
+ if [ "${{ needs.test-matrix.result }}" != "success" ]; then
48
+ echo "Test matrix failed with result: ${{ needs.test-matrix.result }}"
49
+ exit 1
50
+ fi
51
+ echo "All tests passed!"
@@ -0,0 +1,200 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: write
10
+ packages: write
11
+
12
+ jobs:
13
+ release:
14
+ runs-on: ubuntu-latest
15
+ if: github.ref == 'refs/heads/main'
16
+
17
+ steps:
18
+ - name: Checkout code
19
+ uses: actions/checkout@v7
20
+ with:
21
+ fetch-depth: 0
22
+ token: ${{ secrets.RELEASE_TOKEN }}
23
+
24
+ - name: Check for skip-release marker
25
+ id: check_skip
26
+ run: |
27
+ SUBJECT=$(git log -1 --pretty=%s)
28
+ if echo "$SUBJECT" | grep -q '\[skip-release\]'; then
29
+ echo "skip=true" >> $GITHUB_OUTPUT
30
+ echo "Skipping release: $SUBJECT"
31
+ else
32
+ echo "skip=false" >> $GITHUB_OUTPUT
33
+ echo "Proceeding with release check: $SUBJECT"
34
+ fi
35
+
36
+ - name: Set up Node.js
37
+ if: steps.check_skip.outputs.skip != 'true'
38
+ uses: actions/setup-node@v6
39
+ with:
40
+ node-version: "20"
41
+ registry-url: "https://registry.npmjs.org"
42
+ cache: "npm"
43
+
44
+ - name: Install dependencies
45
+ if: steps.check_skip.outputs.skip != 'true'
46
+ run: npm ci
47
+
48
+ - name: Run tests
49
+ if: steps.check_skip.outputs.skip != 'true'
50
+ run: npm test
51
+
52
+ - name: Build
53
+ if: steps.check_skip.outputs.skip != 'true'
54
+ run: npm run build
55
+
56
+ - name: Configure Git
57
+ if: steps.check_skip.outputs.skip != 'true'
58
+ run: |
59
+ git config user.name "github-actions[bot]"
60
+ git config user.email "github-actions[bot]@users.noreply.github.com"
61
+
62
+ - name: Determine next version
63
+ id: version
64
+ if: steps.check_skip.outputs.skip != 'true'
65
+ run: |
66
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
67
+ LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
68
+
69
+ echo "current_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
70
+
71
+ if [ -z "$LAST_TAG" ]; then
72
+ echo "new_release=true" >> $GITHUB_OUTPUT
73
+ echo "new_version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
74
+ echo "bump=initial" >> $GITHUB_OUTPUT
75
+ echo "First release: $CURRENT_VERSION"
76
+ exit 0
77
+ fi
78
+
79
+ COMMITS=$(git log "$LAST_TAG"..HEAD --pretty=%s)
80
+
81
+ if [ -z "$COMMITS" ]; then
82
+ echo "No new commits since $LAST_TAG"
83
+ echo "new_release=false" >> $GITHUB_OUTPUT
84
+ exit 0
85
+ fi
86
+
87
+ BUMP=""
88
+ if echo "$COMMITS" | grep -qiE '^[a-z]+(\(.+\))?!:|BREAKING CHANGE'; then
89
+ BUMP="major"
90
+ elif echo "$COMMITS" | grep -qiE '^feat(\(.+\))?:'; then
91
+ BUMP="minor"
92
+ elif echo "$COMMITS" | grep -qiE '^(fix|perf|refactor)(\(.+\))?:'; then
93
+ BUMP="patch"
94
+ fi
95
+
96
+ if [ -z "$BUMP" ]; then
97
+ echo "No release-worthy commits (feat/fix/perf/refactor) since $LAST_TAG"
98
+ echo "new_release=false" >> $GITHUB_OUTPUT
99
+ exit 0
100
+ fi
101
+
102
+ IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
103
+ case "$BUMP" in
104
+ major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
105
+ minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
106
+ patch) PATCH=$((PATCH + 1)) ;;
107
+ esac
108
+ NEW_VERSION="$MAJOR.$MINOR.$PATCH"
109
+
110
+ echo "new_release=true" >> $GITHUB_OUTPUT
111
+ echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
112
+ echo "bump=$BUMP" >> $GITHUB_OUTPUT
113
+ echo "Detected $BUMP bump: $CURRENT_VERSION → $NEW_VERSION"
114
+
115
+ - name: Bump version in package.json
116
+ if: steps.version.outputs.new_release == 'true'
117
+ run: |
118
+ if [ "${{ steps.version.outputs.bump }}" = "initial" ]; then
119
+ git tag -a "v${{ steps.version.outputs.new_version }}" -m "Release v${{ steps.version.outputs.new_version }}"
120
+ else
121
+ npm version ${{ steps.version.outputs.new_version }} --no-git-tag-version
122
+ git add package.json package-lock.json
123
+ git commit -m "chore(release): ${{ steps.version.outputs.new_version }} [skip-release]"
124
+ git tag -a "v${{ steps.version.outputs.new_version }}" -m "Release v${{ steps.version.outputs.new_version }}"
125
+ fi
126
+
127
+ - name: Push version commit and tag
128
+ if: steps.version.outputs.new_release == 'true'
129
+ run: |
130
+ git push origin main --follow-tags
131
+
132
+ - name: Generate release notes
133
+ id: notes
134
+ if: steps.version.outputs.new_release == 'true'
135
+ run: |
136
+ LAST_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "")
137
+ NEW_VERSION="v${{ steps.version.outputs.new_version }}"
138
+
139
+ {
140
+ echo "notes<<RELEASE_NOTES_EOF"
141
+
142
+ if [ -n "$LAST_TAG" ]; then
143
+ echo "## What's Changed"
144
+ echo ""
145
+
146
+ FEATS=$(git log "$LAST_TAG"..HEAD~1 --pretty="- %s" | grep -iE '^\- feat' || true)
147
+ FIXES=$(git log "$LAST_TAG"..HEAD~1 --pretty="- %s" | grep -iE '^\- fix' || true)
148
+ OTHERS=$(git log "$LAST_TAG"..HEAD~1 --pretty="- %s" | grep -viE '^\- (feat|fix|chore\(release\))' || true)
149
+
150
+ if [ -n "$FEATS" ]; then
151
+ echo "### Features"
152
+ echo "$FEATS"
153
+ echo ""
154
+ fi
155
+ if [ -n "$FIXES" ]; then
156
+ echo "### Bug Fixes"
157
+ echo "$FIXES"
158
+ echo ""
159
+ fi
160
+ if [ -n "$OTHERS" ]; then
161
+ echo "### Other Changes"
162
+ echo "$OTHERS"
163
+ echo ""
164
+ fi
165
+
166
+ echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/$LAST_TAG...$NEW_VERSION"
167
+ else
168
+ echo "Initial release"
169
+ fi
170
+
171
+ echo "RELEASE_NOTES_EOF"
172
+ } >> $GITHUB_OUTPUT
173
+
174
+ - name: Create GitHub Release
175
+ if: steps.version.outputs.new_release == 'true'
176
+ env:
177
+ GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
178
+ run: |
179
+ gh release create "v${{ steps.version.outputs.new_version }}" \
180
+ --title "v${{ steps.version.outputs.new_version }}" \
181
+ --notes "${{ steps.notes.outputs.notes }}" \
182
+ --latest
183
+
184
+ - name: Publish to npm
185
+ if: steps.version.outputs.new_release == 'true'
186
+ env:
187
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
188
+ run: |
189
+ npm publish
190
+
191
+ - name: Release summary
192
+ if: steps.version.outputs.new_release == 'true'
193
+ run: |
194
+ echo "## Release Summary" >> $GITHUB_STEP_SUMMARY
195
+ echo "" >> $GITHUB_STEP_SUMMARY
196
+ echo "Released version: **v${{ steps.version.outputs.new_version }}** (${{ steps.version.outputs.bump }} bump)" >> $GITHUB_STEP_SUMMARY
197
+ echo "" >> $GITHUB_STEP_SUMMARY
198
+ echo "Published to:" >> $GITHUB_STEP_SUMMARY
199
+ echo "- GitHub Releases" >> $GITHUB_STEP_SUMMARY
200
+ echo "- npm (npx installable)" >> $GITHUB_STEP_SUMMARY
@@ -0,0 +1,112 @@
1
+ # Contributing to Facebook MCP Server
2
+
3
+ Thank you for your interest in contributing! This guide will help you get started.
4
+
5
+ ## Development Setup
6
+
7
+ ### Prerequisites
8
+
9
+ - Node.js 18+
10
+ - Git
11
+
12
+ ### Installation
13
+
14
+ ```bash
15
+ git clone https://github.com/luminarylane/facebook-mcp-server.git
16
+ cd facebook-mcp-server
17
+ npm install
18
+ ```
19
+
20
+ ### Environment Setup
21
+
22
+ ```bash
23
+ cp .env.example .env
24
+ # Add your Facebook Page credentials to .env
25
+ ```
26
+
27
+ ## Development Workflow
28
+
29
+ ### 1. Create a Branch
30
+
31
+ ```bash
32
+ git checkout -b feature/your-description
33
+ ```
34
+
35
+ ### 2. Make Changes
36
+
37
+ Follow the existing code style and conventions.
38
+
39
+ ### 3. Test
40
+
41
+ ```bash
42
+ npm test
43
+ ```
44
+
45
+ ### 4. Build
46
+
47
+ ```bash
48
+ npm run build
49
+ ```
50
+
51
+ ### 5. Push and Create PR
52
+
53
+ ```bash
54
+ git push origin your-branch-name
55
+ gh pr create --title "Description" --body "Details..."
56
+ ```
57
+
58
+ ## Code Style
59
+
60
+ - TypeScript strict mode
61
+ - Prettier for formatting
62
+ - Vitest for testing
63
+ - No external SDK dependencies — raw `fetch` against the Graph API
64
+
65
+ ## Testing
66
+
67
+ ```bash
68
+ # Run all tests
69
+ npm test
70
+
71
+ # Type check
72
+ npx tsc --noEmit
73
+ ```
74
+
75
+ ### Writing Tests
76
+
77
+ - Place tests alongside source files as `*.test.ts`
78
+ - Use `vi.stubGlobal("fetch", ...)` to stub network calls
79
+ - Every tool handler should have happy-path and error-path coverage
80
+
81
+ ## Project Structure
82
+
83
+ ```
84
+ facebook-mcp-server/
85
+ ├── src/
86
+ │ ├── index.ts # MCP server + tool registration
87
+ │ ├── client.ts # Graph API HTTP client
88
+ │ ├── create-post.ts # Post creation validation
89
+ │ ├── errors.ts # Error mapping + agent action hints
90
+ │ ├── rate-limiter.ts # Token-bucket rate limiter
91
+ │ ├── response.ts # MCP response helpers
92
+ │ ├── sanitize.ts # Prompt injection protection
93
+ │ ├── lib/
94
+ │ │ ├── index.ts # Public API surface
95
+ │ │ └── insights.ts # SENSE/insights functions
96
+ │ └── *.test.ts # Tests alongside source
97
+ ├── .env.example
98
+ ├── package.json
99
+ ├── tsconfig.json
100
+ └── vitest.config.ts
101
+ ```
102
+
103
+ ## Pull Request Process
104
+
105
+ 1. Ensure tests pass: `npm test`
106
+ 2. Ensure types check: `npx tsc --noEmit`
107
+ 3. Write a clear PR description
108
+ 4. Link to related issues
109
+
110
+ ## License
111
+
112
+ By contributing, you agree that your contributions will be licensed under the MIT License.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Luminary Lane
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,128 @@
1
+ # Facebook Pages MCP Server
2
+
3
+ [![MCP](https://img.shields.io/badge/MCP-1.0-blue)](https://modelcontextprotocol.io)
4
+ [![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)](https://www.typescriptlang.org/)
5
+ [![License](https://img.shields.io/badge/License-MIT-yellow)](LICENSE)
6
+
7
+ A [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server for the Facebook Graph API (Pages). Enables Claude Desktop and other MCP clients to read insights, create posts, manage comments, and more — all through the Facebook Pages API.
8
+
9
+ ## Features
10
+
11
+ ### SENSE Tools (Read)
12
+
13
+ | Tool | Description |
14
+ | ---------------------- | ------------------------------------------------------------------- |
15
+ | `fb_get_page_insights` | Page-level metrics: impressions, reach, engagements, follower count |
16
+ | `fb_get_post_insights` | Post-level metrics: impressions, clicks, reactions |
17
+ | `fb_get_comments` | Comments on a post with author info, likes, replies |
18
+ | `fb_get_page_feed` | Your published posts with permalinks, shares, timestamps |
19
+
20
+ ### ACT Tools (Write)
21
+
22
+ | Tool | Description |
23
+ | ------------------ | ------------------------------------------------------------------------- |
24
+ | `fb_create_post` | Create text, link, photo, or video posts. Supports first-comment for CTAs |
25
+ | `fb_reply_comment` | Reply to a comment on a post |
26
+ | `fb_delete_post` | Delete a post you published |
27
+
28
+ ## Quick Start
29
+
30
+ ### Prerequisites
31
+
32
+ - Node.js 18+
33
+ - A Facebook Page Access Token ([how to get one](https://developers.facebook.com/docs/pages/access-tokens))
34
+ - Your Facebook Page ID
35
+
36
+ ### Install
37
+
38
+ ```bash
39
+ git clone https://github.com/luminarylane/facebook-mcp-server.git
40
+ cd facebook-mcp-server
41
+ npm install
42
+ npm run build
43
+ ```
44
+
45
+ ### Configure
46
+
47
+ ```bash
48
+ cp .env.example .env
49
+ # Edit .env with your credentials:
50
+ # FACEBOOK_ACCESS_TOKEN=your_long_lived_facebook_page_token_here
51
+ # FACEBOOK_PAGE_ID=your_facebook_page_id_here
52
+ ```
53
+
54
+ ### Run
55
+
56
+ ```bash
57
+ # Development
58
+ npm run dev
59
+
60
+ # Production
61
+ npm start
62
+ ```
63
+
64
+ ### Claude Desktop Configuration
65
+
66
+ Add to your `claude_desktop_config.json`:
67
+
68
+ ```json
69
+ {
70
+ "mcpServers": {
71
+ "facebook": {
72
+ "command": "node",
73
+ "args": ["/path/to/facebook-mcp-server/dist/index.js"],
74
+ "env": {
75
+ "FACEBOOK_ACCESS_TOKEN": "your_token_here",
76
+ "FACEBOOK_PAGE_ID": "your_page_id_here"
77
+ }
78
+ }
79
+ }
80
+ }
81
+ ```
82
+
83
+ ## Architecture
84
+
85
+ - **Transport**: stdio (standard MCP protocol)
86
+ - **API**: Facebook Graph API v21.0
87
+ - **Rate Limiting**: Built-in token-bucket rate limiter (200 calls/hour global, 25 publishes/day) with per-tenant isolation
88
+ - **Retry**: Automatic exponential backoff on 429s (up to 3 retries)
89
+ - **Security**: Input sanitization against prompt injection (zero-width chars, whitespace abuse, context flooding)
90
+ - **Error Handling**: Structured errors with actionable `action` hints for agents
91
+
92
+ ## Development
93
+
94
+ ```bash
95
+ # Run tests
96
+ npm test
97
+
98
+ # Development server (auto-reload)
99
+ npm run dev
100
+
101
+ # Type check
102
+ npx tsc --noEmit
103
+
104
+ # Build
105
+ npm run build
106
+ ```
107
+
108
+ ## Testing
109
+
110
+ The test suite covers:
111
+
112
+ - **client.test.ts** — Graph API HTTP client, URL construction, error parsing
113
+ - **create-post.test.ts** — Post creation validation and endpoint routing
114
+ - **errors.test.ts** — Error mapping and agent-action hint strings
115
+ - **first-comment.test.ts** — First-comment partial-success contract
116
+ - **handlers.test.ts** — Full tool handler integration tests
117
+ - **rate-limiter.test.ts** — Token bucket, per-tenant isolation, retry logic
118
+ - **response.test.ts** — MCP response formatting
119
+ - **sanitize.test.ts** — Prompt injection protection
120
+ - **tools.test.ts** — safeHandler and credential resolution
121
+
122
+ ```bash
123
+ npm test
124
+ ```
125
+
126
+ ## License
127
+
128
+ [MIT](LICENSE)
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Facebook Graph API client.
3
+ *
4
+ * Raw fetch wrapper against https://graph.facebook.com/v21.0/.
5
+ * No SDK — keeps dependencies minimal (Occam's Razor).
6
+ * Client instances are cached by credential hash.
7
+ */
8
+ export interface Credentials {
9
+ accessToken: string;
10
+ pageId: string;
11
+ }
12
+ /**
13
+ * Get or create a cached Facebook client.
14
+ */
15
+ export declare function createClient(creds: Credentials): FacebookClient;
16
+ /**
17
+ * Graph API error format from Facebook.
18
+ */
19
+ export interface GraphApiError {
20
+ error: {
21
+ message: string;
22
+ type: string;
23
+ code: number;
24
+ error_subcode?: number;
25
+ fbtrace_id?: string;
26
+ };
27
+ }
28
+ /**
29
+ * Error thrown for Graph API failures. Carries structured error data
30
+ * for suggestAction() to inspect.
31
+ */
32
+ export declare class FacebookApiError extends Error {
33
+ readonly status: number;
34
+ readonly code: number;
35
+ readonly errorSubcode?: number;
36
+ readonly errorType: string;
37
+ readonly retryAfter?: number;
38
+ constructor(status: number, apiError: GraphApiError["error"], retryAfter?: number);
39
+ }
40
+ export declare class FacebookClient {
41
+ readonly accessToken: string;
42
+ readonly pageId: string;
43
+ constructor(creds: Credentials);
44
+ /**
45
+ * Make a GET request to the Graph API.
46
+ */
47
+ get<T = unknown>(path: string, params?: Record<string, string>): Promise<T>;
48
+ /**
49
+ * Make a POST request to the Graph API.
50
+ */
51
+ post<T = unknown>(path: string, body?: Record<string, unknown>): Promise<T>;
52
+ /**
53
+ * Make a DELETE request to the Graph API.
54
+ */
55
+ delete<T = unknown>(path: string): Promise<T>;
56
+ private handleResponse;
57
+ }