@sodiumhq/mcp-pm 0.1.0-beta.2596 → 0.1.0-beta.2600
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 +8 -4
- package/dist/index.js +41 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# @sodiumhq/mcp-pm
|
|
2
2
|
|
|
3
|
-
Model Context Protocol (MCP) server for [Sodium Practice Management](https://sodiumhq.com). Lets AI assistants like Claude Desktop, Claude Code, Cursor, and
|
|
3
|
+
Model Context Protocol (MCP) server for [Sodium Practice Management](https://sodiumhq.com). Lets AI assistants like Claude Desktop, Claude Code, Cursor, and VS Code interact with your Sodium tenant.
|
|
4
|
+
|
|
5
|
+
> **Full setup guide and example prompts:** [sodiumhq.com/features/mcp](https://sodiumhq.com/features/mcp)
|
|
4
6
|
|
|
5
7
|
## Status
|
|
6
8
|
|
|
@@ -8,13 +10,15 @@ Model Context Protocol (MCP) server for [Sodium Practice Management](https://sod
|
|
|
8
10
|
|
|
9
11
|
## Quick start
|
|
10
12
|
|
|
11
|
-
### 1.
|
|
13
|
+
### 1. Grab your API key and tenant code
|
|
14
|
+
|
|
15
|
+
In Sodium, open the **profile menu** (top right) and click **API Keys**. Create a new key and copy it.
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
Your **tenant code** is the subdomain in the URL you use to log in — if you sign in at `acme.sodiumhq.com`, your tenant code is `acme`.
|
|
14
18
|
|
|
15
19
|
### 2. Configure your MCP client
|
|
16
20
|
|
|
17
|
-
**Claude Desktop**
|
|
21
|
+
**Claude Desktop** — edit `~/Library/Application Support/Claude/claude_desktop_config.json` on Mac, or `%APPDATA%\Claude\claude_desktop_config.json` on Windows:
|
|
18
22
|
|
|
19
23
|
```json
|
|
20
24
|
{
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
5
|
import { randomUUID } from "node:crypto";
|
|
@@ -952,14 +953,32 @@ var SodiumApiError = class extends Error {
|
|
|
952
953
|
this.name = "SodiumApiError";
|
|
953
954
|
}
|
|
954
955
|
};
|
|
956
|
+
function sanitizeToken(value) {
|
|
957
|
+
return value.replace(/[\s()\/]+/g, "-").replace(/[^\w.\-]/g, "") || "unknown";
|
|
958
|
+
}
|
|
955
959
|
var SodiumApiClient = class {
|
|
956
|
-
|
|
960
|
+
serverVersion;
|
|
961
|
+
mcpClient = null;
|
|
962
|
+
constructor(ctx, options) {
|
|
957
963
|
this.ctx = ctx;
|
|
964
|
+
this.serverVersion = options.serverVersion;
|
|
958
965
|
client.setConfig({
|
|
959
966
|
baseUrl: ctx.baseUrl,
|
|
960
|
-
headers: {
|
|
967
|
+
headers: {
|
|
968
|
+
"x-api-key": ctx.apiKey,
|
|
969
|
+
"User-Agent": this.buildUserAgent()
|
|
970
|
+
}
|
|
961
971
|
});
|
|
962
972
|
}
|
|
973
|
+
setMcpClientInfo(info) {
|
|
974
|
+
this.mcpClient = info;
|
|
975
|
+
client.setConfig({ headers: { "User-Agent": this.buildUserAgent() } });
|
|
976
|
+
}
|
|
977
|
+
buildUserAgent() {
|
|
978
|
+
const base = `sodiumhq-mcp-pm/${this.serverVersion}`;
|
|
979
|
+
if (!this.mcpClient) return base;
|
|
980
|
+
return `${base} (client=${sanitizeToken(this.mcpClient.name)}/${sanitizeToken(this.mcpClient.version)})`;
|
|
981
|
+
}
|
|
963
982
|
async getPracticeDetails() {
|
|
964
983
|
const correlationId = randomUUID();
|
|
965
984
|
const { data, error, response } = await getPracticeDetails({
|
|
@@ -1197,7 +1216,17 @@ async function buildInstructions(api) {
|
|
|
1197
1216
|
]);
|
|
1198
1217
|
const now = /* @__PURE__ */ new Date();
|
|
1199
1218
|
const lines = [
|
|
1200
|
-
"You are assisting a user of Sodium Practice Management.",
|
|
1219
|
+
"You are assisting a user of Sodium Practice Management — software used by accountancy practices to run their business.",
|
|
1220
|
+
"",
|
|
1221
|
+
"IMPORTANT — who the user is and whose data this is:",
|
|
1222
|
+
"- The user is a member of an accountancy practice (accountant, bookkeeper, partner, or practice staff). They are NEVER the end client.",
|
|
1223
|
+
"- 'Clients' in this system are the businesses and individuals the practice provides services to (accounts, tax, payroll, bookkeeping, advisory, etc.).",
|
|
1224
|
+
"- When the user names an entity (e.g. 'ACME Ltd', 'Greggs Carpentry'), they are almost always referring to one of THEIR CLIENTS — not themselves or their own practice.",
|
|
1225
|
+
"- Tasks, proposals, engagement letters, services and statutory dates in this system describe work the practice does FOR clients.",
|
|
1226
|
+
"",
|
|
1227
|
+
"Always answer from the practice's point of view:",
|
|
1228
|
+
"- For prompts like 'brief me on my call with X' or 'summarise X', produce a practice-side view: what services the practice delivers to X, what work is outstanding, what the user should raise or action with the client.",
|
|
1229
|
+
"- Never frame output as if the named entity is the one being briefed — it is the practice (the user) being briefed ABOUT the client.",
|
|
1201
1230
|
"",
|
|
1202
1231
|
`Today: ${now.toISOString().slice(0, 10)} (${now.toLocaleDateString("en-GB", { weekday: "long" })})`,
|
|
1203
1232
|
`Current UTC time: ${now.toISOString()}`
|
|
@@ -2094,7 +2123,7 @@ function describeFilters(args) {
|
|
|
2094
2123
|
//#endregion
|
|
2095
2124
|
//#region ../mcp-core/src/server.ts
|
|
2096
2125
|
async function buildServer(config) {
|
|
2097
|
-
const api = new SodiumApiClient(config.context);
|
|
2126
|
+
const api = new SodiumApiClient(config.context, { serverVersion: config.serverVersion });
|
|
2098
2127
|
const instructions = await buildInstructions(api);
|
|
2099
2128
|
const server = new McpServer({
|
|
2100
2129
|
name: config.serverName,
|
|
@@ -2103,6 +2132,13 @@ async function buildServer(config) {
|
|
|
2103
2132
|
instructions,
|
|
2104
2133
|
capabilities: { tools: {} }
|
|
2105
2134
|
});
|
|
2135
|
+
server.server.oninitialized = () => {
|
|
2136
|
+
const info = server.server.getClientVersion();
|
|
2137
|
+
if (info?.name && info?.version) api.setMcpClientInfo({
|
|
2138
|
+
name: info.name,
|
|
2139
|
+
version: info.version
|
|
2140
|
+
});
|
|
2141
|
+
};
|
|
2106
2142
|
server.registerTool("get_practice_details", {
|
|
2107
2143
|
title: "Get practice details",
|
|
2108
2144
|
description: "Get a consolidated overview of the practice including name, contact details, client/service/user counts, connections, and settings. Use this when the user asks about their practice, tenant, or wants a summary of their account.",
|
|
@@ -2221,7 +2257,7 @@ function loadContext() {
|
|
|
2221
2257
|
}
|
|
2222
2258
|
//#endregion
|
|
2223
2259
|
//#region src/index.ts
|
|
2224
|
-
const VERSION = "
|
|
2260
|
+
const VERSION = createRequire(import.meta.url)("../package.json").version;
|
|
2225
2261
|
async function main() {
|
|
2226
2262
|
const context = loadContext();
|
|
2227
2263
|
const server = await buildServer({
|