closecadence-mcp 0.2.0 → 0.2.1
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 +2 -2
- package/package.json +1 -1
- package/src/index.js +12 -5
package/README.md
CHANGED
|
@@ -4,11 +4,11 @@ Use your **Close Cadence** real-estate tools from your own Claude. This is a tin
|
|
|
4
4
|
Claude (Desktop / Code) call your Close Cadence tools — listings, CMAs, social, follow-ups, flyers,
|
|
5
5
|
photo enhancement — directly, in your saved voice, on your plan's credits.
|
|
6
6
|
|
|
7
|
-
It's a thin client: it only forwards your authenticated requests to `closecadence.
|
|
7
|
+
It's a thin client: it only forwards your authenticated requests to `tools.closecadence.com`. No tool logic or
|
|
8
8
|
data lives in this package.
|
|
9
9
|
|
|
10
10
|
## Setup (about 1 minute)
|
|
11
|
-
1. Get your key in the portal → **
|
|
11
|
+
1. Get your key in the portal → **Use in Claude** (format `cck.cus_….…`).
|
|
12
12
|
2. Add this to your Claude config (Claude Desktop → Settings → Developer → Edit Config):
|
|
13
13
|
|
|
14
14
|
```json
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Close Cadence MCP server — lets an agent's own Claude call the Close Cadence tools.
|
|
3
|
-
// THIN CLIENT ONLY: it proxies authenticated calls to the licensed API at closecadence.
|
|
3
|
+
// THIN CLIENT ONLY: it proxies authenticated calls to the licensed API at tools.closecadence.com. No business
|
|
4
4
|
// logic, prompts, or comp/CMA data live here (that's protected, server-side IP) — this just forwards the
|
|
5
5
|
// agent's key + inputs and returns the result. Auth: env CC_API_KEY (cck.cus_xxx.yyy). stdio transport.
|
|
6
6
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
@@ -10,7 +10,7 @@ import { writeFileSync, mkdirSync, readFileSync, existsSync } from 'node:fs';
|
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { homedir } from 'node:os';
|
|
12
12
|
|
|
13
|
-
const API = process.env.CC_API_BASE || 'https://closecadence.
|
|
13
|
+
const API = process.env.CC_API_BASE || 'https://tools.closecadence.com';
|
|
14
14
|
const KEY = process.env.CC_API_KEY || '';
|
|
15
15
|
|
|
16
16
|
// ---- Pillar 2: brand + market profiles travel with every job ----
|
|
@@ -50,8 +50,8 @@ const errText = (m) => ({ content: [{ type: 'text', text: m }], isError: true })
|
|
|
50
50
|
async function handle(r) {
|
|
51
51
|
let j = {};
|
|
52
52
|
try { j = await r.json(); } catch { /* non-JSON */ }
|
|
53
|
-
if (r.status === 401) return errText('Your Close Cadence membership isn’t active, or the key is invalid. Renew or rotate your key in the portal (closecadence.
|
|
54
|
-
if (r.status === 402) return errText((j && j.error) || 'You’re out of credits for this month. Top up at closecadence.
|
|
53
|
+
if (r.status === 401) return errText('Your Close Cadence membership isn’t active, or the key is invalid. Renew or rotate your key in the portal (tools.closecadence.com/portal → Use in Claude).');
|
|
54
|
+
if (r.status === 402) return errText((j && j.error) || 'You’re out of credits for this month. Top up at tools.closecadence.com/portal → Credits (or turn on auto-refill).');
|
|
55
55
|
if (r.status === 429) return errText((j && j.error) || 'Rate limited, or this key was flagged for use from too many places. Try again shortly, or rotate it in the portal.');
|
|
56
56
|
if (r.status === 503) return errText((j && j.error) || 'This tool is being set up — check back shortly.');
|
|
57
57
|
if (!r.ok || (j && j.error)) return errText((j && j.error) || ('Close Cadence error ' + r.status + '.'));
|
|
@@ -60,7 +60,7 @@ async function handle(r) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function requireKey() {
|
|
63
|
-
return KEY ? null : errText('No CC_API_KEY set. Add your Close Cadence key (portal →
|
|
63
|
+
return KEY ? null : errText('No CC_API_KEY set. Add your Close Cadence key (portal → Use in Claude) to the MCP config "env", e.g. { "CC_API_KEY": "cck...." }.');
|
|
64
64
|
}
|
|
65
65
|
async function callTool(tool, inputs) {
|
|
66
66
|
const miss = requireKey(); if (miss) return miss;
|
|
@@ -98,6 +98,7 @@ const NAMED = [
|
|
|
98
98
|
['cc_open_house', 'open-house-kit', 'Open-house kit: social + email + neighbor invite + visitor follow-up.', 'address, details'],
|
|
99
99
|
['cc_prospecting', 'prospecting', 'Expired / FSBO outreach: letter + call script + texts.', 'type, context'],
|
|
100
100
|
['cc_listing_presentation', 'listing-presentation', 'Win-the-listing presentation content, slide by slide.', 'address, property, seller_goal, market, differentiators'],
|
|
101
|
+
['cc_avatar_report', 'avatar-marketing-report', 'Two-presenter AI-avatar marketing report script from your numbers (Studio) — feeds the avatar video renderer.', 'agent_name, co_presenter, market, period, total_volume, sales_count, avg_price, top_sale, wins, focus'],
|
|
101
102
|
];
|
|
102
103
|
for (const [name, id, desc, fields] of NAMED) {
|
|
103
104
|
server.registerTool(
|
|
@@ -118,6 +119,12 @@ server.registerTool('cc_property', { title: 'cc_property', description: 'Propert
|
|
|
118
119
|
server.registerTool('cc_flyer', { title: 'cc_flyer', description: 'Generate a "just listed" card / flyer image (readable text).', inputSchema: { prompt: z.string().optional(), headline: z.string().optional() } }, async (a) => callPath('/api/media/flyer', a));
|
|
119
120
|
server.registerTool('cc_enhance', { title: 'cc_enhance', description: 'Enhance a listing photo (sky replacement / HDR / declutter).', inputSchema: { imageUrl: z.string().url() } }, async (a) => callPath('/api/media/enhance', a));
|
|
120
121
|
|
|
122
|
+
// Avatar Studio (Studio tier): generate a branded talking-head marketing VIDEO or REEL in your own
|
|
123
|
+
// voice from your numbers. Rendering is async — cc_avatar_render enqueues + returns a job id; poll
|
|
124
|
+
// cc_avatar_status for the finished video url. Each render uses plan credits (video 500 / reel 120).
|
|
125
|
+
server.registerTool('cc_avatar_render', { title: 'cc_avatar_render', description: 'Generate an on-brand AI-avatar marketing VIDEO or REEL in your voice from your numbers (Studio). Async — returns a job id; poll cc_avatar_status. Uses plan credits.', inputSchema: { kind: z.enum(['report', 'reel']).optional().describe('report (full) or reel (short)'), fmt: z.enum(['video', 'reel']).optional().describe('video = 16:9, reel = 9:16') } }, async ({ kind, fmt }) => callPath('/api/avatar/request', { kind: kind || 'report', fmt: fmt || (kind === 'reel' ? 'reel' : 'video') }));
|
|
126
|
+
server.registerTool('cc_avatar_status', { title: 'cc_avatar_status', description: 'Check an avatar render job by id (or omit to list your recent renders). Returns status + the finished video url when ready.', inputSchema: { job: z.string().optional().describe('job id from cc_avatar_render') } }, async ({ job }) => getPath('/api/avatar/status' + (job ? ('?job=' + encodeURIComponent(job)) : '')));
|
|
127
|
+
|
|
121
128
|
// Discovery + escape hatch.
|
|
122
129
|
server.registerTool('cc_list_tools', { title: 'cc_list_tools', description: 'List every Close Cadence tool available to your plan.', inputSchema: {} }, async () => {
|
|
123
130
|
const miss = requireKey(); if (miss) return miss;
|