fetchpet-mcp-server 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 ADDED
@@ -0,0 +1,181 @@
1
+ # Fetch Pet MCP Server
2
+
3
+ MCP server for Fetch Pet insurance claims management using Playwright automation. Submit claims, track claim status, and view claim details including EOB and invoices.
4
+
5
+ ## Features
6
+
7
+ - **Prepare Claims** - Fill out claim forms with validation (without submitting)
8
+ - **Submit Claims** - Actually submit prepared claims with explicit user confirmation
9
+ - **View All Claims** - See all claims (active and historical) in a single call
10
+ - **Claim Details** - Get detailed information including EOB and invoice downloads
11
+
12
+ ## Tools
13
+
14
+ | Tool | Description |
15
+ | ------------------------- | ------------------------------------------------------------------- |
16
+ | `prepare_claim_to_submit` | Prepare a claim form for submission (validates but does NOT submit) |
17
+ | `submit_claim` | Submit a prepared claim (requires user confirmation token) |
18
+ | `get_claims` | Get all claims (both active/pending and historical/completed) |
19
+ | `get_claim_details` | Get detailed claim info including EOB and invoice downloads |
20
+
21
+ ## Setup
22
+
23
+ ### Prerequisites
24
+
25
+ - Node.js 18+
26
+ - A Fetch Pet account (create one at [fetchpet.com](https://fetchpet.com))
27
+
28
+ ### Environment Variables
29
+
30
+ | Variable | Required | Description | Default |
31
+ | ----------------------- | -------- | ---------------------------------- | ------------------------- |
32
+ | `FETCHPET_USERNAME` | Yes | Your Fetch Pet account email | - |
33
+ | `FETCHPET_PASSWORD` | Yes | Your Fetch Pet account password | - |
34
+ | `HEADLESS` | No | Run browser in headless mode | `true` |
35
+ | `TIMEOUT` | No | Browser operation timeout (ms) | `30000` |
36
+ | `FETCHPET_DOWNLOAD_DIR` | No | Directory to save downloaded files | `/tmp/fetchpet-downloads` |
37
+
38
+ ### Claude Desktop
39
+
40
+ Make sure you have your Fetch Pet account credentials ready.
41
+
42
+ Then proceed to the setup instructions below. If this is your first time using MCP Servers, you'll want to make sure you have the [Claude Desktop application](https://claude.ai/download) and follow the [official MCP setup instructions](https://modelcontextprotocol.io/quickstart/user).
43
+
44
+ #### Manual Setup
45
+
46
+ You're going to need Node working on your machine so you can run `npx` commands in your terminal. If you don't have Node, you can install it from [nodejs.org](https://nodejs.org/en/download).
47
+
48
+ macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
49
+
50
+ Windows: `%APPDATA%\Claude\claude_desktop_config.json`
51
+
52
+ Modify your `claude_desktop_config.json` file to add the following:
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "fetchpet": {
58
+ "command": "npx",
59
+ "args": ["-y", "fetchpet-mcp-server"],
60
+ "env": {
61
+ "FETCHPET_USERNAME": "your-email@example.com",
62
+ "FETCHPET_PASSWORD": "your-password"
63
+ }
64
+ }
65
+ }
66
+ }
67
+ ```
68
+
69
+ Restart Claude Desktop and you should be ready to go!
70
+
71
+ ## Usage Examples
72
+
73
+ ### Submit a new claim
74
+
75
+ ```
76
+ "Submit a claim for my dog Buddy - I have an invoice from Test Vet Clinic for $150 dated January 15th for an annual checkup"
77
+ ```
78
+
79
+ The assistant will:
80
+
81
+ 1. Use `prepare_claim_to_submit` to fill out the form
82
+ 2. Show you exactly what will be submitted
83
+ 3. Ask for your explicit confirmation
84
+ 4. Only then call `submit_claim` with the confirmation token
85
+
86
+ ### View claims
87
+
88
+ ```
89
+ "Show me my active claims"
90
+ "What claims are pending?"
91
+ "Show my claim history"
92
+ "What's the status of my recent claims?"
93
+ ```
94
+
95
+ ### Get claim details
96
+
97
+ ```
98
+ "Get details for claim ABC123"
99
+ "Download the EOB for my last claim"
100
+ "Show me the invoice for that claim"
101
+ ```
102
+
103
+ ## How It Works
104
+
105
+ This MCP server uses Playwright to automate a browser session with Fetch Pet:
106
+
107
+ 1. **On server start**: Launches a browser in the background and logs into your Fetch Pet account
108
+ 2. **Browser session persists**: All subsequent tool calls reuse the same logged-in session
109
+ 3. **Smart navigation**: Tools navigate to the appropriate pages as needed
110
+ 4. **Stealth mode**: Uses playwright-extra with stealth plugin to avoid bot detection
111
+
112
+ ### Claim Submission Safety
113
+
114
+ The claim submission process is designed with safety in mind:
115
+
116
+ 1. **`prepare_claim_to_submit`** fills out the form and validates everything but does NOT click submit
117
+ 2. It returns a unique confirmation token
118
+ 3. **`submit_claim`** requires this token, ensuring explicit user confirmation
119
+ 4. Without the correct token, claims cannot be submitted
120
+
121
+ ## Development
122
+
123
+ ```bash
124
+ # Install dependencies
125
+ npm run install-all
126
+
127
+ # Run in development mode
128
+ npm run dev
129
+
130
+ # Build
131
+ npm run build
132
+
133
+ # Run tests
134
+ npm test
135
+
136
+ # Run integration tests
137
+ npm run test:integration
138
+
139
+ # Run manual tests (requires real credentials)
140
+ npm run test:manual
141
+
142
+ # Lint
143
+ npm run lint
144
+ ```
145
+
146
+ ## Project Structure
147
+
148
+ ```
149
+ fetchpet/
150
+ ├── local/ # Local server implementation
151
+ │ └── src/
152
+ │ └── index.ts # Entry point with env validation
153
+ ├── shared/ # Shared business logic
154
+ │ └── src/
155
+ │ ├── server.ts # FetchPetClient with Playwright automation
156
+ │ ├── tools.ts # MCP tool definitions
157
+ │ ├── types.ts # TypeScript types
158
+ │ └── logging.ts # Logging utilities
159
+ ├── tests/ # Test suites
160
+ ├── package.json # Root workspace config
161
+ └── README.md
162
+ ```
163
+
164
+ ## Security Notes
165
+
166
+ - Your Fetch Pet credentials are used only to log into your account
167
+ - The browser session runs locally on your machine
168
+ - No credentials are transmitted to any third-party services
169
+ - Consider using environment variables rather than hardcoding credentials
170
+ - Downloaded documents (EOB, invoices) are saved to the configured download directory
171
+
172
+ ## Limitations
173
+
174
+ - Requires a valid Fetch Pet account
175
+ - Browser automation may occasionally fail if Fetch Pet updates their website
176
+ - Some operations require navigating between pages which takes time
177
+ - The website uses a React app, so dynamic content loading may require waits
178
+
179
+ ## License
180
+
181
+ MIT
@@ -0,0 +1,166 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Integration test entry point that uses a mock client.
4
+ * This allows testing MCP server functionality without actual browser automation.
5
+ */
6
+ import { readFileSync } from 'fs';
7
+ import { dirname, join } from 'path';
8
+ import { fileURLToPath } from 'url';
9
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
10
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
11
+ import { createRegisterTools } from '../shared/tools.js';
12
+ import { logServerStart, logError } from '../shared/logging.js';
13
+ // Read version from package.json
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+ const packageJsonPath = join(__dirname, '..', 'package.json');
16
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
17
+ const VERSION = packageJson.version;
18
+ // =============================================================================
19
+ // MOCK CLIENT FOR INTEGRATION TESTS
20
+ // =============================================================================
21
+ class MockFetchPetClient {
22
+ mockToken = null;
23
+ async initialize() {
24
+ // Mock initialization - no actual browser
25
+ }
26
+ async prepareClaimToSubmit(petName, invoiceDate, invoiceAmount, providerName, claimDescription, _invoiceFilePath, _medicalRecordsPath) {
27
+ this.mockToken = 'mock-confirmation-token-12345';
28
+ return {
29
+ petName,
30
+ invoiceDate,
31
+ invoiceAmount,
32
+ providerName,
33
+ claimDescription,
34
+ isReadyToSubmit: true,
35
+ validationErrors: [],
36
+ confirmationMessage: `IMPORTANT: This claim has been prepared but NOT submitted yet.
37
+
38
+ To submit this claim, call submit_claim with confirmation_token: "${this.mockToken}"
39
+
40
+ Claim Details:
41
+ - Pet: ${petName}
42
+ - Invoice Date: ${invoiceDate}
43
+ - Amount: ${invoiceAmount}
44
+ - Provider: ${providerName}
45
+ - Description: ${claimDescription}
46
+
47
+ The user MUST explicitly confirm they want to submit this claim before calling submit_claim.`,
48
+ };
49
+ }
50
+ async submitClaim(confirmationToken) {
51
+ if (confirmationToken !== this.mockToken) {
52
+ return {
53
+ success: false,
54
+ message: 'Invalid or expired confirmation token',
55
+ };
56
+ }
57
+ this.mockToken = null;
58
+ return {
59
+ success: true,
60
+ message: 'Claim submitted successfully (mock)',
61
+ claimId: 'MOCK-CLAIM-001',
62
+ confirmationNumber: 'MOCK-CONF-12345',
63
+ };
64
+ }
65
+ async getClaims() {
66
+ return [
67
+ {
68
+ claimId: 'MOCK-ACTIVE-001',
69
+ petName: 'Buddy',
70
+ claimDate: '2025-01-15',
71
+ claimAmount: '$250.00',
72
+ status: 'pending',
73
+ description: 'Annual checkup',
74
+ providerName: 'Mock Vet Clinic',
75
+ },
76
+ {
77
+ claimId: 'MOCK-ACTIVE-002',
78
+ petName: 'Luna',
79
+ claimDate: '2025-01-20',
80
+ claimAmount: '$175.50',
81
+ status: 'processing',
82
+ description: 'Dental cleaning',
83
+ providerName: 'Pet Dental Care',
84
+ },
85
+ {
86
+ claimId: 'MOCK-HIST-001',
87
+ petName: 'Buddy',
88
+ claimDate: '2024-12-01',
89
+ claimAmount: '$500.00',
90
+ status: 'approved',
91
+ description: 'Emergency surgery',
92
+ providerName: 'Emergency Vet Hospital',
93
+ },
94
+ {
95
+ claimId: 'MOCK-HIST-002',
96
+ petName: 'Luna',
97
+ claimDate: '2024-11-15',
98
+ claimAmount: '$85.00',
99
+ status: 'paid',
100
+ description: 'Vaccinations',
101
+ providerName: 'Mock Vet Clinic',
102
+ },
103
+ ];
104
+ }
105
+ async getClaimDetails(claimId) {
106
+ return {
107
+ claimId,
108
+ petName: 'Buddy',
109
+ claimDate: '2025-01-15',
110
+ claimAmount: '$250.00',
111
+ status: 'pending',
112
+ description: 'Annual checkup and vaccinations',
113
+ providerName: 'Mock Vet Clinic',
114
+ reimbursementAmount: '$200.00',
115
+ deductible: '$50.00',
116
+ copay: '$0.00',
117
+ eobSummary: 'EOB available for download',
118
+ invoiceSummary: 'Invoice available for download',
119
+ };
120
+ }
121
+ async getCurrentUrl() {
122
+ return 'https://my.fetchpet.com/dashboard';
123
+ }
124
+ async close() {
125
+ // Mock cleanup
126
+ }
127
+ getConfig() {
128
+ return {
129
+ username: 'mock@example.com',
130
+ password: 'mock-password',
131
+ headless: true,
132
+ timeout: 30000,
133
+ downloadDir: '/tmp/mock-downloads',
134
+ };
135
+ }
136
+ }
137
+ // =============================================================================
138
+ // MAIN ENTRY POINT
139
+ // =============================================================================
140
+ async function main() {
141
+ const server = new Server({
142
+ name: 'fetchpet-mcp-server',
143
+ version: VERSION,
144
+ }, {
145
+ capabilities: {
146
+ tools: {},
147
+ },
148
+ });
149
+ // Create mock client
150
+ const mockClient = new MockFetchPetClient();
151
+ // Register tools with mock client factory
152
+ const registerTools = createRegisterTools(() => mockClient, async () => {
153
+ await mockClient.initialize();
154
+ return mockClient;
155
+ });
156
+ registerTools(server);
157
+ // Start server with stdio transport
158
+ const transport = new StdioServerTransport();
159
+ await server.connect(transport);
160
+ logServerStart('Fetch Pet (Mock Mode)');
161
+ }
162
+ // Run the server
163
+ main().catch((error) => {
164
+ logError('main', error);
165
+ process.exit(1);
166
+ });
package/build/index.js ADDED
@@ -0,0 +1,129 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync } from 'fs';
3
+ import { dirname, join } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6
+ import { createMCPServer } from '../shared/index.js';
7
+ import { logServerStart, logError, logWarning } from '../shared/logging.js';
8
+ // Read version from package.json
9
+ const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const packageJsonPath = join(__dirname, '..', 'package.json');
11
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
12
+ const VERSION = packageJson.version;
13
+ // =============================================================================
14
+ // ENVIRONMENT VALIDATION
15
+ // =============================================================================
16
+ function validateEnvironment() {
17
+ const required = [
18
+ {
19
+ name: 'FETCHPET_USERNAME',
20
+ description: 'Your Fetch Pet account email address',
21
+ example: 'user@example.com',
22
+ },
23
+ {
24
+ name: 'FETCHPET_PASSWORD',
25
+ description: 'Your Fetch Pet account password',
26
+ example: 'your-password',
27
+ },
28
+ ];
29
+ const optional = [
30
+ {
31
+ name: 'HEADLESS',
32
+ description: 'Run browser in headless mode (true/false)',
33
+ defaultValue: 'true',
34
+ },
35
+ {
36
+ name: 'TIMEOUT',
37
+ description: 'Default timeout for browser operations in milliseconds',
38
+ defaultValue: '30000',
39
+ },
40
+ {
41
+ name: 'FETCHPET_DOWNLOAD_DIR',
42
+ description: 'Directory to save downloaded documents (EOB, invoices)',
43
+ defaultValue: '/tmp/fetchpet-downloads',
44
+ },
45
+ ];
46
+ const missing = required.filter(({ name }) => !process.env[name]);
47
+ if (missing.length > 0) {
48
+ logError('validateEnvironment', 'Missing required environment variables:');
49
+ missing.forEach(({ name, description, example }) => {
50
+ console.error(` - ${name}: ${description}`);
51
+ console.error(` Example: ${example}`);
52
+ });
53
+ if (optional.length > 0) {
54
+ console.error('\nOptional environment variables:');
55
+ optional.forEach(({ name, description, defaultValue }) => {
56
+ const defaultStr = defaultValue ? ` (default: ${defaultValue})` : '';
57
+ console.error(` - ${name}: ${description}${defaultStr}`);
58
+ });
59
+ }
60
+ console.error('\n----------------------------------------');
61
+ console.error('Please set the required environment variables and try again.');
62
+ console.error('\nExample commands:');
63
+ missing.forEach(({ name, example }) => {
64
+ console.error(` export ${name}="${example}"`);
65
+ });
66
+ console.error('----------------------------------------\n');
67
+ process.exit(1);
68
+ }
69
+ // Log configuration
70
+ const headless = process.env.HEADLESS !== 'false';
71
+ const timeout = process.env.TIMEOUT || '30000';
72
+ const downloadDir = process.env.FETCHPET_DOWNLOAD_DIR || '/tmp/fetchpet-downloads';
73
+ if (!headless) {
74
+ logWarning('config', 'Running in non-headless mode - browser window will be visible');
75
+ }
76
+ if (process.env.TIMEOUT) {
77
+ logWarning('config', `Custom timeout configured: ${timeout}ms`);
78
+ }
79
+ if (process.env.FETCHPET_DOWNLOAD_DIR) {
80
+ logWarning('config', `Custom download directory: ${downloadDir}`);
81
+ }
82
+ }
83
+ // =============================================================================
84
+ // MAIN ENTRY POINT
85
+ // =============================================================================
86
+ async function main() {
87
+ // Step 1: Validate environment variables
88
+ validateEnvironment();
89
+ // Step 2: Create server using factory
90
+ const { server, registerHandlers, cleanup, startBackgroundLogin } = createMCPServer({
91
+ version: VERSION,
92
+ });
93
+ // Step 3: Register all handlers (tools)
94
+ await registerHandlers(server);
95
+ // Step 4: Set up graceful shutdown
96
+ const handleShutdown = async () => {
97
+ logWarning('shutdown', 'Received shutdown signal, closing browser...');
98
+ await cleanup();
99
+ process.exit(0);
100
+ };
101
+ process.on('SIGINT', handleShutdown);
102
+ process.on('SIGTERM', handleShutdown);
103
+ // Step 5: Start server with stdio transport
104
+ const transport = new StdioServerTransport();
105
+ await server.connect(transport);
106
+ logServerStart('Fetch Pet');
107
+ // Step 6: Start background login process
108
+ // This kicks off Playwright and performs login without blocking the stdio connection.
109
+ // If login fails, the server will close with an error.
110
+ logWarning('login', 'Starting background login to Fetch Pet...');
111
+ startBackgroundLogin((error) => {
112
+ // Login failed - log error and exit
113
+ logError('login', `Background login failed: ${error.message}`);
114
+ logError('login', 'Server shutting down due to authentication failure.');
115
+ // Clean up and exit with error
116
+ cleanup()
117
+ .catch((cleanupError) => {
118
+ logError('cleanup', `Error during cleanup: ${cleanupError}`);
119
+ })
120
+ .finally(() => {
121
+ process.exit(1);
122
+ });
123
+ });
124
+ }
125
+ // Run the server
126
+ main().catch((error) => {
127
+ logError('main', error);
128
+ process.exit(1);
129
+ });
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "fetchpet-mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for Fetch Pet insurance claims management with Playwright automation",
5
+ "main": "build/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "fetchpet-mcp-server": "./build/index.js"
9
+ },
10
+ "files": [
11
+ "build/**/*.js",
12
+ "build/**/*.d.ts",
13
+ "shared/**/*.js",
14
+ "shared/**/*.d.ts",
15
+ "README.md"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc && npm run build:integration",
19
+ "build:integration": "tsc -p tsconfig.integration.json",
20
+ "start": "node build/index.js",
21
+ "dev": "tsx src/index.ts",
22
+ "predev": "cd ../shared && npm run build && cd ../local && node setup-dev.js",
23
+ "prebuild": "cd ../shared && npm run build && cd ../local && node setup-dev.js",
24
+ "prepublishOnly": "node prepare-publish.js && node ../scripts/prepare-npm-readme.js",
25
+ "lint": "eslint . --ext .ts,.tsx",
26
+ "lint:fix": "eslint . --ext .ts,.tsx --fix",
27
+ "format": "prettier --write .",
28
+ "format:check": "prettier --check .",
29
+ "stage-publish": "npm version"
30
+ },
31
+ "dependencies": {
32
+ "@modelcontextprotocol/sdk": "^1.19.1",
33
+ "playwright": "^1.49.1",
34
+ "playwright-extra": "^4.3.6",
35
+ "puppeteer-extra-plugin-stealth": "^2.11.2",
36
+ "zod": "^3.24.1"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^22.10.6",
40
+ "tsx": "^4.19.4",
41
+ "typescript": "^5.7.3"
42
+ },
43
+ "keywords": [
44
+ "mcp",
45
+ "fetch",
46
+ "pet",
47
+ "insurance",
48
+ "claims",
49
+ "playwright"
50
+ ],
51
+ "author": "PulseMCP",
52
+ "license": "MIT"
53
+ }
@@ -0,0 +1,5 @@
1
+ export { createMCPServer, FetchPetClient, type IFetchPetClient, type ClientFactory, type CreateMCPServerOptions, } from './server.js';
2
+ export { createRegisterTools } from './tools.js';
3
+ export type { Claim, ClaimDetails, ClaimSubmissionData, ClaimSubmissionResult, FetchPetConfig, } from './types.js';
4
+ export { logServerStart, logError, logWarning, logDebug } from './logging.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,6 @@
1
+ // Core server exports
2
+ export { createMCPServer, FetchPetClient, } from './server.js';
3
+ // Tools exports
4
+ export { createRegisterTools } from './tools.js';
5
+ // Logging exports (re-exported for convenience)
6
+ export { logServerStart, logError, logWarning, logDebug } from './logging.js';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Logging utilities for consistent output across MCP servers
3
+ */
4
+ /**
5
+ * Log server startup message
6
+ */
7
+ export declare function logServerStart(serverName: string, transport?: string): void;
8
+ /**
9
+ * Log an error with context
10
+ */
11
+ export declare function logError(context: string, error: unknown): void;
12
+ /**
13
+ * Log a warning
14
+ */
15
+ export declare function logWarning(context: string, message: string): void;
16
+ /**
17
+ * Log debug information (only in development)
18
+ */
19
+ export declare function logDebug(context: string, message: string): void;
20
+ //# sourceMappingURL=logging.d.ts.map
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Logging utilities for consistent output across MCP servers
3
+ */
4
+ /**
5
+ * Log server startup message
6
+ */
7
+ export function logServerStart(serverName, transport = 'stdio') {
8
+ console.error(`MCP server ${serverName} running on ${transport}`);
9
+ }
10
+ /**
11
+ * Log an error with context
12
+ */
13
+ export function logError(context, error) {
14
+ const message = error instanceof Error ? error.message : String(error);
15
+ const stack = error instanceof Error ? error.stack : undefined;
16
+ console.error(`[ERROR] ${context}: ${message}`);
17
+ if (stack) {
18
+ console.error(stack);
19
+ }
20
+ }
21
+ /**
22
+ * Log a warning
23
+ */
24
+ export function logWarning(context, message) {
25
+ console.error(`[WARN] ${context}: ${message}`);
26
+ }
27
+ /**
28
+ * Log debug information (only in development)
29
+ */
30
+ export function logDebug(context, message) {
31
+ if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
32
+ console.error(`[DEBUG] ${context}: ${message}`);
33
+ }
34
+ }