exponential-mcp 0.7.0 → 0.8.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/dist/cli.js +0 -0
- package/dist/index.js +26 -12
- package/dist/runTools.d.ts +29 -0
- package/dist/runTools.js +98 -0
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
File without changes
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { homedir } from 'os';
|
|
|
12
12
|
import { dirname, join } from 'path';
|
|
13
13
|
import { fileURLToPath } from 'url';
|
|
14
14
|
import { DOMAINS, runDomainTool, toolDefinition } from './domains/index.js';
|
|
15
|
+
import { RUN_TOOLS, RUN_TOOL_NAMES, runContextFromEnv, runRunTool } from './runTools.js';
|
|
15
16
|
const LEGACY_CONFIG_PATH = join(homedir(), '.config', 'exponential-mcp', 'config.json');
|
|
16
17
|
// Report the real package version rather than a hand-maintained literal that drifts.
|
|
17
18
|
const PKG_VERSION = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf-8')).version;
|
|
@@ -73,23 +74,29 @@ function migrateLegacyConfig() {
|
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
function loadClientConfig() {
|
|
77
|
+
// An explicit key in the environment wins over the stored config: a process
|
|
78
|
+
// that starts this server for a specific principal — `exponential runner
|
|
79
|
+
// start` launching it for an Assistant run with the Assistant's agent key —
|
|
80
|
+
// must act as that principal, not as whoever last ran `init` here.
|
|
81
|
+
const envToken = process.env.EXPONENTIAL_API_KEY || process.env.EXPONENTIAL_API_TOKEN;
|
|
82
|
+
if (envToken) {
|
|
83
|
+
assertTokenIsUsable(envToken, 'EXPONENTIAL_API_KEY');
|
|
84
|
+
return {
|
|
85
|
+
token: envToken,
|
|
86
|
+
apiUrl: process.env.EXPONENTIAL_API_URL ||
|
|
87
|
+
process.env.EXPONENTIAL_BASE_URL ||
|
|
88
|
+
'https://www.exponential.im',
|
|
89
|
+
};
|
|
90
|
+
}
|
|
76
91
|
migrateLegacyConfig();
|
|
77
92
|
if (configStore.isAuthenticated()) {
|
|
78
93
|
const config = configStore.loadConfig();
|
|
79
94
|
assertTokenIsUsable(config.token, 'stored config');
|
|
80
95
|
return { token: config.token, apiUrl: config.apiUrl };
|
|
81
96
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
'https://www.exponential.im';
|
|
86
|
-
if (!token) {
|
|
87
|
-
console.error('Error: No API key found.');
|
|
88
|
-
console.error('Run "npx exponential-mcp init" to set up, or set EXPONENTIAL_API_KEY env var.');
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
91
|
-
assertTokenIsUsable(token, 'EXPONENTIAL_API_KEY');
|
|
92
|
-
return { token, apiUrl };
|
|
97
|
+
console.error('Error: No API key found.');
|
|
98
|
+
console.error('Run "npx exponential-mcp init" to set up, or set EXPONENTIAL_API_KEY env var.');
|
|
99
|
+
process.exit(1);
|
|
93
100
|
}
|
|
94
101
|
/**
|
|
95
102
|
* `undefined` = leave the field alone, `null` = clear it, otherwise a Date.
|
|
@@ -569,13 +576,20 @@ async function main() {
|
|
|
569
576
|
},
|
|
570
577
|
});
|
|
571
578
|
// List available tools
|
|
579
|
+
// Started for an Agent run (`exponential runner start` sets EXPONENTIAL_RUN_ID):
|
|
580
|
+
// the session gets the three run tools on top of everything else.
|
|
581
|
+
const runContext = runContextFromEnv();
|
|
572
582
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
573
|
-
tools: [...TOOLS, ...[...DOMAINS.values()].map(toolDefinition)],
|
|
583
|
+
tools: [...(runContext ? RUN_TOOLS : []), ...TOOLS, ...[...DOMAINS.values()].map(toolDefinition)],
|
|
574
584
|
}));
|
|
575
585
|
// Handle tool calls
|
|
576
586
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
577
587
|
const { name, arguments: args } = request.params;
|
|
578
588
|
try {
|
|
589
|
+
if (runContext && RUN_TOOL_NAMES.has(name)) {
|
|
590
|
+
const result = await runRunTool(client, runContext, name, args);
|
|
591
|
+
return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
|
|
592
|
+
}
|
|
579
593
|
const domain = DOMAINS.get(name);
|
|
580
594
|
if (domain) {
|
|
581
595
|
const result = await runDomainTool(domain, client, args);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run tools (Exponential ADR-0067, Agent PRD V2): how a session the local
|
|
3
|
+
* runner spawned talks to its Agent run. They exist only when the server was
|
|
4
|
+
* started for a run — `EXPONENTIAL_RUN_ID` set in the environment by
|
|
5
|
+
* `exponential runner start` (which also passes the Assistant's agent key as
|
|
6
|
+
* `EXPONENTIAL_API_KEY` and its name as `EXPONENTIAL_RUNNER_ID`). Outside a
|
|
7
|
+
* run they are not listed, so an ordinary MCP session never sees them.
|
|
8
|
+
*
|
|
9
|
+
* - `report_progress` — a one-line note for the run's transcript. The runner
|
|
10
|
+
* records it from the session's own tool stream, so this tool only
|
|
11
|
+
* acknowledges; nothing is written from here.
|
|
12
|
+
* - `ask_owner` — asks the owner and PAUSES the run: the app posts the question
|
|
13
|
+
* as a comment mentioning the owner and parks the run; the owner's reply
|
|
14
|
+
* starts a new run. The session must stop after calling it.
|
|
15
|
+
* - `finish_run` — closes the run with a public summary and whether the action
|
|
16
|
+
* is ready for the owner to confirm. The run never completes the action.
|
|
17
|
+
*/
|
|
18
|
+
import type { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
19
|
+
import type { ExponentialClient } from 'exponential-sdk';
|
|
20
|
+
export interface RunContext {
|
|
21
|
+
runId: string;
|
|
22
|
+
runnerId?: string;
|
|
23
|
+
}
|
|
24
|
+
/** The run this server was started for, if any. */
|
|
25
|
+
export declare function runContextFromEnv(env?: NodeJS.ProcessEnv): RunContext | null;
|
|
26
|
+
export declare const RUN_TOOLS: Tool[];
|
|
27
|
+
export declare const RUN_TOOL_NAMES: Set<string>;
|
|
28
|
+
/** Dispatch one run tool. The caller has already checked `RUN_TOOL_NAMES.has(name)`. */
|
|
29
|
+
export declare function runRunTool(client: Pick<ExponentialClient, 'agentRuns'>, context: RunContext, name: string, args: Record<string, unknown> | undefined): Promise<unknown>;
|
package/dist/runTools.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run tools (Exponential ADR-0067, Agent PRD V2): how a session the local
|
|
3
|
+
* runner spawned talks to its Agent run. They exist only when the server was
|
|
4
|
+
* started for a run — `EXPONENTIAL_RUN_ID` set in the environment by
|
|
5
|
+
* `exponential runner start` (which also passes the Assistant's agent key as
|
|
6
|
+
* `EXPONENTIAL_API_KEY` and its name as `EXPONENTIAL_RUNNER_ID`). Outside a
|
|
7
|
+
* run they are not listed, so an ordinary MCP session never sees them.
|
|
8
|
+
*
|
|
9
|
+
* - `report_progress` — a one-line note for the run's transcript. The runner
|
|
10
|
+
* records it from the session's own tool stream, so this tool only
|
|
11
|
+
* acknowledges; nothing is written from here.
|
|
12
|
+
* - `ask_owner` — asks the owner and PAUSES the run: the app posts the question
|
|
13
|
+
* as a comment mentioning the owner and parks the run; the owner's reply
|
|
14
|
+
* starts a new run. The session must stop after calling it.
|
|
15
|
+
* - `finish_run` — closes the run with a public summary and whether the action
|
|
16
|
+
* is ready for the owner to confirm. The run never completes the action.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
20
|
+
/** The run this server was started for, if any. */
|
|
21
|
+
export function runContextFromEnv(env = process.env) {
|
|
22
|
+
const runId = env.EXPONENTIAL_RUN_ID?.trim();
|
|
23
|
+
if (!runId)
|
|
24
|
+
return null;
|
|
25
|
+
return { runId, runnerId: env.EXPONENTIAL_RUNNER_ID?.trim() || undefined };
|
|
26
|
+
}
|
|
27
|
+
const reportProgressParams = z.object({
|
|
28
|
+
text: z.string().min(1).max(500).describe('One line, present tense: what you are doing now'),
|
|
29
|
+
});
|
|
30
|
+
const askOwnerParams = z.object({
|
|
31
|
+
question: z
|
|
32
|
+
.string()
|
|
33
|
+
.min(1)
|
|
34
|
+
.max(10000)
|
|
35
|
+
.describe('The question, with enough context that the owner can answer from their inbox'),
|
|
36
|
+
});
|
|
37
|
+
const finishRunParams = z.object({
|
|
38
|
+
summary: z
|
|
39
|
+
.string()
|
|
40
|
+
.min(1)
|
|
41
|
+
.max(10000)
|
|
42
|
+
.describe('What you did, found or delegated. Public: the requester, the owner and their teammates read it'),
|
|
43
|
+
readyToClose: z
|
|
44
|
+
.boolean()
|
|
45
|
+
.describe('true when the work is done and the owner only needs to confirm; false when follow-up is needed'),
|
|
46
|
+
});
|
|
47
|
+
function schema(params) {
|
|
48
|
+
const { $schema: _ignored, ...rest } = zodToJsonSchema(params, {
|
|
49
|
+
target: 'jsonSchema7',
|
|
50
|
+
$refStrategy: 'none',
|
|
51
|
+
});
|
|
52
|
+
return rest;
|
|
53
|
+
}
|
|
54
|
+
export const RUN_TOOLS = [
|
|
55
|
+
{
|
|
56
|
+
name: 'report_progress',
|
|
57
|
+
description: 'Post a one-line progress note to this run\'s transcript (owner-visible only; nobody is notified). Use it when you move to a new phase of the work.',
|
|
58
|
+
inputSchema: schema(reportProgressParams),
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'ask_owner',
|
|
62
|
+
description: 'Ask your owner a question you cannot answer yourself and PAUSE this run. The question is posted on the action mentioning the owner; their reply resumes the work in a new run. This must be your LAST call: do not call finish_run after it and do not keep working — stop immediately.',
|
|
63
|
+
inputSchema: schema(askOwnerParams),
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'finish_run',
|
|
67
|
+
description: 'Finish this run. Call it exactly once, as your last action, with a public summary and whether the action is ready for the owner to close. Never mark the action complete yourself; the owner confirms from their inbox.',
|
|
68
|
+
inputSchema: schema(finishRunParams),
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
export const RUN_TOOL_NAMES = new Set(RUN_TOOLS.map((t) => t.name));
|
|
72
|
+
/** Dispatch one run tool. The caller has already checked `RUN_TOOL_NAMES.has(name)`. */
|
|
73
|
+
export async function runRunTool(client, context, name, args) {
|
|
74
|
+
switch (name) {
|
|
75
|
+
case 'report_progress': {
|
|
76
|
+
const { text } = reportProgressParams.parse(args ?? {});
|
|
77
|
+
return { ok: true, text };
|
|
78
|
+
}
|
|
79
|
+
case 'ask_owner': {
|
|
80
|
+
const { question } = askOwnerParams.parse(args ?? {});
|
|
81
|
+
const result = await client.agentRuns.finish(context.runId, { status: 'WAITING_ON_OWNER', question }, context.runnerId);
|
|
82
|
+
return {
|
|
83
|
+
...result,
|
|
84
|
+
stop: true,
|
|
85
|
+
message: result.finished
|
|
86
|
+
? 'Your question was posted and the run is now waiting on your owner. Stop here: make no further tool calls and end your turn.'
|
|
87
|
+
: 'The run is no longer running (it was cancelled); nothing was posted. Stop here.',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
case 'finish_run': {
|
|
91
|
+
const { summary, readyToClose } = finishRunParams.parse(args ?? {});
|
|
92
|
+
const result = await client.agentRuns.finish(context.runId, { status: 'SUCCEEDED', summary, readyToClose }, context.runnerId);
|
|
93
|
+
return { ...result, message: result.finished ? 'Run finished. End your turn.' : 'The run had already ended; nothing changed. End your turn.' };
|
|
94
|
+
}
|
|
95
|
+
default:
|
|
96
|
+
throw new Error(`Unknown run tool "${name}"`);
|
|
97
|
+
}
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "exponential-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "MCP server for Exponential - connect Claude to your projects, actions, goals, CRM, tickets and more",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
28
28
|
"commander": "^12.0.0",
|
|
29
|
-
"exponential-sdk": "^1.
|
|
29
|
+
"exponential-sdk": "^1.21.0",
|
|
30
30
|
"zod": "^3.22.0",
|
|
31
31
|
"zod-to-json-schema": "^3.25.2"
|
|
32
32
|
},
|