@opencapstack/mcp-server 0.1.3 → 0.1.4
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/auth.d.ts.map +1 -1
- package/dist/auth.js +5 -1
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +17 -5
- package/dist/client.js.map +1 -1
- package/dist/schema.d.ts +13 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +22 -0
- package/dist/schema.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +5 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/dilution.d.ts.map +1 -1
- package/dist/tools/dilution.js +6 -13
- package/dist/tools/dilution.js.map +1 -1
- package/dist/tools/documents.d.ts.map +1 -1
- package/dist/tools/documents.js +3 -2
- package/dist/tools/documents.js.map +1 -1
- package/dist/tools/equityGrants.d.ts +3 -0
- package/dist/tools/equityGrants.d.ts.map +1 -0
- package/dist/tools/equityGrants.js +145 -0
- package/dist/tools/equityGrants.js.map +1 -0
- package/dist/tools/equityPlans.d.ts.map +1 -1
- package/dist/tools/equityPlans.js +30 -21
- package/dist/tools/equityPlans.js.map +1 -1
- package/dist/tools/financialReports.d.ts.map +1 -1
- package/dist/tools/financialReports.js +33 -17
- package/dist/tools/financialReports.js.map +1 -1
- package/dist/tools/meta.d.ts +3 -0
- package/dist/tools/meta.d.ts.map +1 -0
- package/dist/tools/meta.js +136 -0
- package/dist/tools/meta.js.map +1 -0
- package/dist/tools/safes.d.ts.map +1 -1
- package/dist/tools/safes.js +59 -42
- package/dist/tools/safes.js.map +1 -1
- package/dist/tools/shareClasses.d.ts.map +1 -1
- package/dist/tools/shareClasses.js +31 -23
- package/dist/tools/shareClasses.js.map +1 -1
- package/dist/tools/stakeholders.d.ts.map +1 -1
- package/dist/tools/stakeholders.js +51 -17
- package/dist/tools/stakeholders.js.map +1 -1
- package/dist/tools/valuations.d.ts.map +1 -1
- package/dist/tools/valuations.js +30 -17
- package/dist/tools/valuations.js.map +1 -1
- package/dist/tools/waterfall.d.ts.map +1 -1
- package/dist/tools/waterfall.js +7 -16
- package/dist/tools/waterfall.js.map +1 -1
- package/package.json +1 -1
- package/src/auth.ts +7 -1
- package/src/client.ts +23 -6
- package/src/schema.ts +28 -0
- package/src/server.ts +5 -1
- package/src/tools/dilution.ts +12 -13
- package/src/tools/documents.ts +3 -2
- package/src/tools/equityGrants.ts +154 -0
- package/src/tools/equityPlans.ts +30 -21
- package/src/tools/financialReports.ts +37 -17
- package/src/tools/meta.ts +142 -0
- package/src/tools/safes.ts +59 -42
- package/src/tools/shareClasses.ts +34 -23
- package/src/tools/stakeholders.ts +51 -17
- package/src/tools/valuations.ts +29 -20
- package/src/tools/waterfall.ts +13 -16
package/dist/tools/valuations.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { coerceInt, coerceFloat } from '../schema.js';
|
|
2
3
|
export const valuationTools = [
|
|
3
4
|
{
|
|
4
5
|
name: 'get_latest_valuation',
|
|
@@ -7,7 +8,9 @@ export const valuationTools = [
|
|
|
7
8
|
companyId: z.string().describe('Company ID'),
|
|
8
9
|
}),
|
|
9
10
|
handler: async (input, client) => {
|
|
10
|
-
const { data } = await client.get(
|
|
11
|
+
const { data } = await client.get('/api/v1/valuations/latest', {
|
|
12
|
+
params: { companyId: input.companyId },
|
|
13
|
+
});
|
|
11
14
|
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
12
15
|
},
|
|
13
16
|
},
|
|
@@ -16,7 +19,7 @@ export const valuationTools = [
|
|
|
16
19
|
description: 'Get the historical valuation timeline for the company.',
|
|
17
20
|
inputSchema: z.object({
|
|
18
21
|
companyId: z.string().describe('Company ID'),
|
|
19
|
-
limit:
|
|
22
|
+
limit: coerceInt('Max results to return').optional().default(20),
|
|
20
23
|
}),
|
|
21
24
|
handler: async (input, client) => {
|
|
22
25
|
const { data } = await client.get('/api/v1/valuations', { params: input });
|
|
@@ -35,15 +38,8 @@ export const valuationTools = [
|
|
|
35
38
|
.enum(['409A', 'board_approved', 'preferred_round', 'other'])
|
|
36
39
|
.describe('Type of valuation'),
|
|
37
40
|
valuationDate: z.string().describe('Effective date in ISO 8601 format (YYYY-MM-DD)'),
|
|
38
|
-
commonStockFMV:
|
|
39
|
-
|
|
40
|
-
.transform((v) => parseFloat(String(v)))
|
|
41
|
-
.describe('Fair market value per common share in USD'),
|
|
42
|
-
postMoneyValuation: z
|
|
43
|
-
.union([z.number(), z.string()])
|
|
44
|
-
.transform((v) => parseFloat(String(v)))
|
|
45
|
-
.optional()
|
|
46
|
-
.describe('Total post-money company valuation in USD'),
|
|
41
|
+
commonStockFMV: coerceFloat('Fair market value per common share in USD'),
|
|
42
|
+
postMoneyValuation: coerceFloat('Total post-money company valuation in USD').optional(),
|
|
47
43
|
provider: z
|
|
48
44
|
.string()
|
|
49
45
|
.optional()
|
|
@@ -55,12 +51,29 @@ export const valuationTools = [
|
|
|
55
51
|
.describe('URL to the valuation report document'),
|
|
56
52
|
}),
|
|
57
53
|
handler: async (input, client) => {
|
|
58
|
-
const { data } = await client.post('/api/v1/valuations', input);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
54
|
+
const { data: created } = await client.post('/api/v1/valuations', input);
|
|
55
|
+
const id = created.row_id ?? created._id;
|
|
56
|
+
try {
|
|
57
|
+
const { data: confirmed } = await client.get(`/api/v1/valuations/${id}`);
|
|
58
|
+
return {
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: 'text',
|
|
62
|
+
text: `Valuation recorded:\n${JSON.stringify(confirmed, null, 2)}\n\nID for follow-up operations: ${id}`,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: 'text',
|
|
72
|
+
text: `Valuation recorded (could not confirm persisted state — verify with get_valuation_history):\n${JSON.stringify(created, null, 2)}`,
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
64
77
|
},
|
|
65
78
|
},
|
|
66
79
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"valuations.js","sourceRoot":"","sources":["../../src/tools/valuations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"valuations.js","sourceRoot":"","sources":["../../src/tools/valuations.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waterfall.d.ts","sourceRoot":"","sources":["../../src/tools/waterfall.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"waterfall.d.ts","sourceRoot":"","sources":["../../src/tools/waterfall.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,eAAO,MAAM,cAAc,EAAE,cAAc,EAwC1C,CAAC"}
|
package/dist/tools/waterfall.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { coerceFloat, coerceBool } from '../schema.js';
|
|
2
3
|
export const waterfallTools = [
|
|
3
4
|
{
|
|
4
5
|
name: 'run_waterfall_analysis',
|
|
@@ -7,29 +8,19 @@ export const waterfallTools = [
|
|
|
7
8
|
'Returns proceeds per stakeholder and per share class at the given exit amount.',
|
|
8
9
|
inputSchema: z.object({
|
|
9
10
|
companyId: z.string().describe('Company ID'),
|
|
10
|
-
exitAmount:
|
|
11
|
-
.number()
|
|
12
|
-
.positive()
|
|
13
|
-
.describe('Total exit/acquisition proceeds in USD'),
|
|
11
|
+
exitAmount: coerceFloat('Total exit/acquisition proceeds in USD'),
|
|
14
12
|
exitType: z
|
|
15
13
|
.enum(['acquisition', 'ipo', 'dissolution'])
|
|
16
14
|
.optional()
|
|
17
15
|
.default('acquisition')
|
|
18
16
|
.describe('Type of exit event'),
|
|
19
|
-
deductTransactionCosts:
|
|
20
|
-
.boolean()
|
|
17
|
+
deductTransactionCosts: coerceBool('Whether to deduct estimated transaction costs before distribution')
|
|
21
18
|
.optional()
|
|
22
|
-
.default(false)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
.number()
|
|
19
|
+
.default(false),
|
|
20
|
+
transactionCostsAmount: coerceFloat('Transaction costs amount in USD (used if deductTransactionCosts is true)').optional(),
|
|
21
|
+
includeOptionPoolSweep: coerceBool('Whether to include pre-exit option pool sweep')
|
|
26
22
|
.optional()
|
|
27
|
-
.
|
|
28
|
-
includeOptionPoolSweep: z
|
|
29
|
-
.boolean()
|
|
30
|
-
.optional()
|
|
31
|
-
.default(false)
|
|
32
|
-
.describe('Whether to include pre-exit option pool sweep'),
|
|
23
|
+
.default(false),
|
|
33
24
|
asOfDate: z
|
|
34
25
|
.string()
|
|
35
26
|
.optional()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"waterfall.js","sourceRoot":"","sources":["../../src/tools/waterfall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"waterfall.js","sourceRoot":"","sources":["../../src/tools/waterfall.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAGvD,MAAM,CAAC,MAAM,cAAc,GAAqB;IAC9C;QACE,IAAI,EAAE,wBAAwB;QAC9B,WAAW,EACT,iFAAiF;YACjF,yFAAyF;YACzF,gFAAgF;QAClF,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC;YAC5C,UAAU,EAAE,WAAW,CAAC,wCAAwC,CAAC;YACjE,QAAQ,EAAE,CAAC;iBACR,IAAI,CAAC,CAAC,aAAa,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;iBAC3C,QAAQ,EAAE;iBACV,OAAO,CAAC,aAAa,CAAC;iBACtB,QAAQ,CAAC,oBAAoB,CAAC;YACjC,sBAAsB,EAAE,UAAU,CAChC,mEAAmE,CACpE;iBACE,QAAQ,EAAE;iBACV,OAAO,CAAC,KAAK,CAAC;YACjB,sBAAsB,EAAE,WAAW,CACjC,0EAA0E,CAC3E,CAAC,QAAQ,EAAE;YACZ,sBAAsB,EAAE,UAAU,CAChC,+CAA+C,CAChD;iBACE,QAAQ,EAAE;iBACV,OAAO,CAAC,KAAK,CAAC;YACjB,QAAQ,EAAE,CAAC;iBACR,MAAM,EAAE;iBACR,QAAQ,EAAE;iBACV,QAAQ,CAAC,8EAA8E,CAAC;SAC5F,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;YAC/B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE,KAAK,CAAC,CAAC;YACvE,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;aACjE,CAAC;QACJ,CAAC;KACF;CACF,CAAC"}
|
package/package.json
CHANGED
package/src/auth.ts
CHANGED
|
@@ -15,5 +15,11 @@ export function getApiKey(): string {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function getBaseUrl(): string {
|
|
18
|
-
|
|
18
|
+
const url = process.env.OPENCAP_BASE_URL ?? 'https://api.opencapstack.com';
|
|
19
|
+
if (url.includes('/api/v1')) {
|
|
20
|
+
process.stderr.write(
|
|
21
|
+
`Warning: OPENCAP_BASE_URL should not include /api/v1 — tools already prefix this path. Current value: ${url}\n`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return url;
|
|
19
25
|
}
|
package/src/client.ts
CHANGED
|
@@ -20,25 +20,42 @@ export function createClient(apiKey: string): AxiosInstance {
|
|
|
20
20
|
(response) => response,
|
|
21
21
|
(error) => {
|
|
22
22
|
const status = error.response?.status;
|
|
23
|
+
const message =
|
|
24
|
+
error.response?.data?.message ??
|
|
25
|
+
error.response?.data?.error ??
|
|
26
|
+
error.message ??
|
|
27
|
+
'Unknown error';
|
|
28
|
+
const path = error.config?.url ?? '';
|
|
23
29
|
|
|
24
30
|
if (status === 401) {
|
|
25
31
|
throw new McpError(
|
|
26
32
|
ErrorCode.InvalidRequest,
|
|
27
|
-
'API key rejected or expired
|
|
33
|
+
'API key rejected or expired. Regenerate at https://app.opencapstack.com/settings, or run the whoami tool to test your current key.'
|
|
28
34
|
);
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
if (status === 403) {
|
|
32
38
|
throw new McpError(
|
|
33
39
|
ErrorCode.InvalidRequest,
|
|
34
|
-
'Access denied
|
|
40
|
+
'Access denied. Check that your token companyId matches the companyId in your request.'
|
|
35
41
|
);
|
|
36
42
|
}
|
|
37
43
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
44
|
+
if (status === 404) {
|
|
45
|
+
const idHint = path.match(/\/([^/]+)$/)
|
|
46
|
+
? ' Make sure you are using the domain ID field (e.g. safeId, row_id) from a list_* call, not the _id field.'
|
|
47
|
+
: '';
|
|
48
|
+
throw new McpError(ErrorCode.InvalidRequest, `Record not found.${idHint}`);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (status === 500) {
|
|
52
|
+
throw new McpError(
|
|
53
|
+
ErrorCode.InternalError,
|
|
54
|
+
`Server error saving record: ${message}. Check that all referenced IDs exist (e.g. equityPlanId, employeeId, companyId) and try again.`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
throw new McpError(ErrorCode.InternalError, `OpenCap API error (${status}): ${message}`);
|
|
42
59
|
}
|
|
43
60
|
);
|
|
44
61
|
|
package/src/schema.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Zod coercion helpers for MCP tool input schemas.
|
|
3
|
+
*
|
|
4
|
+
* MCP clients (including Claude Code) send all arguments as strings. These
|
|
5
|
+
* helpers accept both the native type and a string representation and coerce
|
|
6
|
+
* to the appropriate JavaScript primitive so tools work correctly regardless
|
|
7
|
+
* of how the client serialises values.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
|
|
12
|
+
export const coerceInt = (description: string) =>
|
|
13
|
+
z
|
|
14
|
+
.union([z.number(), z.string()])
|
|
15
|
+
.transform((v) => parseInt(String(v), 10))
|
|
16
|
+
.describe(description);
|
|
17
|
+
|
|
18
|
+
export const coerceFloat = (description: string) =>
|
|
19
|
+
z
|
|
20
|
+
.union([z.number(), z.string()])
|
|
21
|
+
.transform((v) => parseFloat(String(v)))
|
|
22
|
+
.describe(description);
|
|
23
|
+
|
|
24
|
+
export const coerceBool = (description: string) =>
|
|
25
|
+
z
|
|
26
|
+
.union([z.boolean(), z.string()])
|
|
27
|
+
.transform((v) => v === true || v === 'true')
|
|
28
|
+
.describe(description);
|
package/src/server.ts
CHANGED
|
@@ -22,12 +22,16 @@ import { valuationTools } from './tools/valuations.js';
|
|
|
22
22
|
import { dilutionTools } from './tools/dilution.js';
|
|
23
23
|
import { waterfallTools } from './tools/waterfall.js';
|
|
24
24
|
import { financialReportTools } from './tools/financialReports.js';
|
|
25
|
+
import { equityGrantTools } from './tools/equityGrants.js';
|
|
26
|
+
import { metaTools } from './tools/meta.js';
|
|
25
27
|
import { type ToolDefinition } from './types.js';
|
|
26
28
|
|
|
27
29
|
const ALL_TOOLS: ToolDefinition[] = [
|
|
30
|
+
...metaTools,
|
|
28
31
|
...stakeholderTools,
|
|
29
32
|
...shareClassTools,
|
|
30
33
|
...equityPlanTools,
|
|
34
|
+
...equityGrantTools,
|
|
31
35
|
...safeTools,
|
|
32
36
|
...documentTools,
|
|
33
37
|
...valuationTools,
|
|
@@ -40,7 +44,7 @@ export function createServer(client: AxiosInstance): Server {
|
|
|
40
44
|
const server = new Server(
|
|
41
45
|
{
|
|
42
46
|
name: 'opencap-mcp',
|
|
43
|
-
version: '0.1.
|
|
47
|
+
version: '0.1.4',
|
|
44
48
|
},
|
|
45
49
|
{
|
|
46
50
|
capabilities: {
|
package/src/tools/dilution.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { coerceInt, coerceBool } from '../schema.js';
|
|
2
3
|
import { type ToolDefinition } from '../types.js';
|
|
3
4
|
|
|
4
5
|
export const dilutionTools: ToolDefinition[] = [
|
|
@@ -9,21 +10,19 @@ export const dilutionTools: ToolDefinition[] = [
|
|
|
9
10
|
'Returns pre- and post-dilution ownership percentages for each stakeholder.',
|
|
10
11
|
inputSchema: z.object({
|
|
11
12
|
companyId: z.string().describe('Company ID'),
|
|
12
|
-
newSharesIssued:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
.boolean()
|
|
13
|
+
newSharesIssued: coerceInt(
|
|
14
|
+
'Number of new shares to be issued in the scenario'
|
|
15
|
+
),
|
|
16
|
+
includeOptionPool: coerceBool(
|
|
17
|
+
'Whether to include unissued option pool shares in the denominator'
|
|
18
|
+
)
|
|
19
19
|
.optional()
|
|
20
|
-
.default(false)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
.default(false),
|
|
21
|
+
includeSafes: coerceBool(
|
|
22
|
+
'Whether to include SAFEs in conversion when calculating dilution'
|
|
23
|
+
)
|
|
24
24
|
.optional()
|
|
25
|
-
.default(true)
|
|
26
|
-
.describe('Whether to include SAFEs in conversion when calculating dilution'),
|
|
25
|
+
.default(true),
|
|
27
26
|
}),
|
|
28
27
|
handler: async (input, client) => {
|
|
29
28
|
const { data } = await client.post('/api/v1/dilution/calculate', input);
|
package/src/tools/documents.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { coerceInt } from '../schema.js';
|
|
2
3
|
import { type ToolDefinition } from '../types.js';
|
|
3
4
|
|
|
4
5
|
export const documentTools: ToolDefinition[] = [
|
|
@@ -23,7 +24,7 @@ export const documentTools: ToolDefinition[] = [
|
|
|
23
24
|
.string()
|
|
24
25
|
.optional()
|
|
25
26
|
.describe('Filter documents associated with a stakeholder'),
|
|
26
|
-
limit:
|
|
27
|
+
limit: coerceInt('Max results to return').optional().default(50),
|
|
27
28
|
}),
|
|
28
29
|
handler: async (input, client) => {
|
|
29
30
|
const { data } = await client.get('/api/v1/documents', { params: input });
|
|
@@ -61,7 +62,7 @@ export const documentTools: ToolDefinition[] = [
|
|
|
61
62
|
])
|
|
62
63
|
.optional()
|
|
63
64
|
.describe('Filter by document type'),
|
|
64
|
-
limit:
|
|
65
|
+
limit: coerceInt('Max results to return').optional().default(20),
|
|
65
66
|
}),
|
|
66
67
|
handler: async (input, client) => {
|
|
67
68
|
const { data } = await client.get('/api/v1/documents/search', { params: input });
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { coerceInt, coerceFloat } from '../schema.js';
|
|
3
|
+
import { type ToolDefinition } from '../types.js';
|
|
4
|
+
|
|
5
|
+
export const equityGrantTools: ToolDefinition[] = [
|
|
6
|
+
{
|
|
7
|
+
name: 'list_equity_grants',
|
|
8
|
+
description:
|
|
9
|
+
'List all equity grants (options, RSAs, RSUs, etc.) for a company. ' +
|
|
10
|
+
'Use the `grantId` field from results for follow-up operations, not the `_id` field.',
|
|
11
|
+
inputSchema: z.object({
|
|
12
|
+
companyId: z.string().describe('Company ID'),
|
|
13
|
+
limit: coerceInt('Max results to return').optional().default(50),
|
|
14
|
+
}),
|
|
15
|
+
handler: async (input, client) => {
|
|
16
|
+
const { data } = await client.get('/api/v1/equity-grants', { params: input });
|
|
17
|
+
const grants = data.grants ?? data;
|
|
18
|
+
return {
|
|
19
|
+
content: [{ type: 'text', text: JSON.stringify(grants, null, 2) }],
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'get_equity_grant',
|
|
25
|
+
description:
|
|
26
|
+
'Get details for a specific equity grant by ID. ' +
|
|
27
|
+
'Use the `grantId` field from `list_equity_grants`, not the `_id` field.',
|
|
28
|
+
inputSchema: z.object({
|
|
29
|
+
id: z
|
|
30
|
+
.string()
|
|
31
|
+
.describe('Grant ID — use the `grantId` field from list_equity_grants, not `_id`'),
|
|
32
|
+
}),
|
|
33
|
+
handler: async (input, client) => {
|
|
34
|
+
const { data } = await client.get(`/api/v1/equity-grants/${input.id}`);
|
|
35
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'create_equity_grant',
|
|
40
|
+
description:
|
|
41
|
+
'Create a new equity grant for an employee or advisor. ' +
|
|
42
|
+
'Use the `grantId` field from list_equity_grants for follow-up operations, not the `_id` field.',
|
|
43
|
+
inputSchema: z.object({
|
|
44
|
+
companyId: z.string().describe('Company ID'),
|
|
45
|
+
employeeId: z
|
|
46
|
+
.string()
|
|
47
|
+
.describe('Stakeholder ID of the grantee — use the `row_id` from list_stakeholders'),
|
|
48
|
+
equityPlanId: z
|
|
49
|
+
.string()
|
|
50
|
+
.describe('Equity plan ID — use the `row_id` from list_equity_plans'),
|
|
51
|
+
grantType: z
|
|
52
|
+
.enum(['ISO', 'NSO', 'RSA', 'RSU', 'SAR', 'other'])
|
|
53
|
+
.describe('Type of equity grant'),
|
|
54
|
+
numberOfShares: coerceInt('Number of shares in this grant'),
|
|
55
|
+
grantDate: z.string().describe('Grant date in ISO 8601 format (YYYY-MM-DD)'),
|
|
56
|
+
vestingStartDate: z
|
|
57
|
+
.string()
|
|
58
|
+
.optional()
|
|
59
|
+
.describe('Vesting start date in ISO 8601 format (YYYY-MM-DD). Defaults to grantDate.'),
|
|
60
|
+
strikePrice: coerceFloat(
|
|
61
|
+
'Exercise/strike price per share in USD (required for options)'
|
|
62
|
+
).optional(),
|
|
63
|
+
vestingSchedule: z
|
|
64
|
+
.object({
|
|
65
|
+
totalMonths: coerceInt('Total vesting period in months'),
|
|
66
|
+
cliffMonths: coerceInt('Cliff period in months'),
|
|
67
|
+
})
|
|
68
|
+
.optional()
|
|
69
|
+
.describe('Vesting schedule for this grant. Overrides the equity plan default.'),
|
|
70
|
+
notes: z.string().optional().describe('Free-text notes about this grant'),
|
|
71
|
+
}),
|
|
72
|
+
handler: async (input, client) => {
|
|
73
|
+
const { data: created } = await client.post('/api/v1/equity-grants', input);
|
|
74
|
+
const id = created.grantId ?? created.row_id ?? created._id;
|
|
75
|
+
try {
|
|
76
|
+
const { data: confirmed } = await client.get(`/api/v1/equity-grants/${id}`);
|
|
77
|
+
return {
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: 'text',
|
|
81
|
+
text: `Equity grant created:\n${JSON.stringify(confirmed, null, 2)}\n\nID for follow-up operations: ${id}`,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
} catch {
|
|
86
|
+
return {
|
|
87
|
+
content: [
|
|
88
|
+
{
|
|
89
|
+
type: 'text',
|
|
90
|
+
text: `Equity grant created (could not confirm persisted state — verify with get_equity_grant):\n${JSON.stringify(created, null, 2)}`,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'update_equity_grant',
|
|
99
|
+
description:
|
|
100
|
+
'Update the status of an equity grant (e.g. approve, cancel, mark as exercised). ' +
|
|
101
|
+
'Use the `grantId` field from `list_equity_grants`, not the `_id` field.',
|
|
102
|
+
inputSchema: z.object({
|
|
103
|
+
id: z
|
|
104
|
+
.string()
|
|
105
|
+
.describe('Grant ID — use the `grantId` field from list_equity_grants, not `_id`'),
|
|
106
|
+
status: z
|
|
107
|
+
.enum(['pending', 'approved', 'active', 'exercised', 'cancelled', 'expired'])
|
|
108
|
+
.describe('New status for the grant'),
|
|
109
|
+
}),
|
|
110
|
+
handler: async (input, client) => {
|
|
111
|
+
const { id, ...body } = input;
|
|
112
|
+
const { data: updated } = await client.patch(
|
|
113
|
+
`/api/v1/equity-grants/${id}/status`,
|
|
114
|
+
body
|
|
115
|
+
);
|
|
116
|
+
try {
|
|
117
|
+
const { data: confirmed } = await client.get(`/api/v1/equity-grants/${id}`);
|
|
118
|
+
return {
|
|
119
|
+
content: [
|
|
120
|
+
{
|
|
121
|
+
type: 'text',
|
|
122
|
+
text: `Equity grant updated:\n${JSON.stringify(confirmed, null, 2)}\n\nID for follow-up operations: ${id}`,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
} catch {
|
|
127
|
+
return {
|
|
128
|
+
content: [
|
|
129
|
+
{
|
|
130
|
+
type: 'text',
|
|
131
|
+
text: `Equity grant updated (could not confirm persisted state — verify with get_equity_grant):\n${JSON.stringify(updated, null, 2)}`,
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
name: 'get_vesting_schedule',
|
|
140
|
+
description:
|
|
141
|
+
'Get the full vesting schedule for a specific equity grant — shows each vesting event, ' +
|
|
142
|
+
'cliff date, and cumulative shares vested over time. ' +
|
|
143
|
+
'Use the `grantId` field from `list_equity_grants`, not the `_id` field.',
|
|
144
|
+
inputSchema: z.object({
|
|
145
|
+
id: z
|
|
146
|
+
.string()
|
|
147
|
+
.describe('Grant ID — use the `grantId` field from list_equity_grants, not `_id`'),
|
|
148
|
+
}),
|
|
149
|
+
handler: async (input, client) => {
|
|
150
|
+
const { data } = await client.get(`/api/v1/equity-grants/${input.id}/vesting`);
|
|
151
|
+
return { content: [{ type: 'text', text: JSON.stringify(data, null, 2) }] };
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
];
|
package/src/tools/equityPlans.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { coerceInt } from '../schema.js';
|
|
2
3
|
import { type ToolDefinition } from '../types.js';
|
|
3
4
|
|
|
4
5
|
export const equityPlanTools: ToolDefinition[] = [
|
|
@@ -8,7 +9,7 @@ export const equityPlanTools: ToolDefinition[] = [
|
|
|
8
9
|
'List all equity plans (stock option plans, RSU plans, etc.) in the company.',
|
|
9
10
|
inputSchema: z.object({
|
|
10
11
|
companyId: z.string().optional().describe('Filter by company ID'),
|
|
11
|
-
limit:
|
|
12
|
+
limit: coerceInt('Max results to return').optional().default(50),
|
|
12
13
|
}),
|
|
13
14
|
handler: async (input, client) => {
|
|
14
15
|
const { data } = await client.get('/api/v1/equity-plans', { params: input });
|
|
@@ -20,9 +21,10 @@ export const equityPlanTools: ToolDefinition[] = [
|
|
|
20
21
|
},
|
|
21
22
|
{
|
|
22
23
|
name: 'get_equity_plan',
|
|
23
|
-
description:
|
|
24
|
+
description:
|
|
25
|
+
'Get details for a specific equity plan by ID. Use the `row_id` field from `list_equity_plans`.',
|
|
24
26
|
inputSchema: z.object({
|
|
25
|
-
id: z.string().describe('Equity plan ID'),
|
|
27
|
+
id: z.string().describe('Equity plan ID — use the `row_id` field from list_equity_plans'),
|
|
26
28
|
}),
|
|
27
29
|
handler: async (input, client) => {
|
|
28
30
|
const { data } = await client.get(`/api/v1/equity-plans/${input.id}`);
|
|
@@ -37,10 +39,7 @@ export const equityPlanTools: ToolDefinition[] = [
|
|
|
37
39
|
planType: z
|
|
38
40
|
.enum(['ISO', 'NSO', 'RSA', 'RSU', 'SAR', 'other'])
|
|
39
41
|
.describe('Type of equity plan'),
|
|
40
|
-
sharesReserved:
|
|
41
|
-
.union([z.number(), z.string()])
|
|
42
|
-
.transform((v) => parseInt(String(v), 10))
|
|
43
|
-
.describe('Number of shares reserved for this plan'),
|
|
42
|
+
sharesReserved: coerceInt('Number of shares reserved for this plan'),
|
|
44
43
|
companyId: z.string().describe('Company ID this plan belongs to'),
|
|
45
44
|
expirationDate: z
|
|
46
45
|
.string()
|
|
@@ -48,25 +47,35 @@ export const equityPlanTools: ToolDefinition[] = [
|
|
|
48
47
|
.describe('Plan expiration date in ISO 8601 format (YYYY-MM-DD)'),
|
|
49
48
|
defaultVestingSchedule: z
|
|
50
49
|
.object({
|
|
51
|
-
totalMonths:
|
|
52
|
-
|
|
53
|
-
.transform((v) => parseInt(String(v), 10))
|
|
54
|
-
.describe('Total vesting period in months'),
|
|
55
|
-
cliffMonths: z
|
|
56
|
-
.union([z.number(), z.string()])
|
|
57
|
-
.transform((v) => parseInt(String(v), 10))
|
|
58
|
-
.describe('Cliff period in months'),
|
|
50
|
+
totalMonths: coerceInt('Total vesting period in months'),
|
|
51
|
+
cliffMonths: coerceInt('Cliff period in months'),
|
|
59
52
|
})
|
|
60
53
|
.optional()
|
|
61
54
|
.describe('Default vesting schedule for grants under this plan'),
|
|
62
55
|
}),
|
|
63
56
|
handler: async (input, client) => {
|
|
64
|
-
const { data } = await client.post('/api/v1/equity-plans', input);
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
57
|
+
const { data: created } = await client.post('/api/v1/equity-plans', input);
|
|
58
|
+
const id = created.row_id ?? created._id;
|
|
59
|
+
try {
|
|
60
|
+
const { data: confirmed } = await client.get(`/api/v1/equity-plans/${id}`);
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: 'text',
|
|
65
|
+
text: `Equity plan created:\n${JSON.stringify(confirmed, null, 2)}\n\nID for follow-up operations: ${id}`,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
} catch {
|
|
70
|
+
return {
|
|
71
|
+
content: [
|
|
72
|
+
{
|
|
73
|
+
type: 'text',
|
|
74
|
+
text: `Equity plan created (could not confirm persisted state — verify with get_equity_plan):\n${JSON.stringify(created, null, 2)}`,
|
|
75
|
+
},
|
|
76
|
+
],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
70
79
|
},
|
|
71
80
|
},
|
|
72
81
|
];
|