@ezmodo/mcp-server 0.13.5 → 0.14.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/handlers/auth.js +160 -0
- package/handlers/index.js +2 -0
- package/http.js +6 -1
- package/index.js +21 -52
- package/lib/auth-guidance.js +67 -0
- package/lib/cli-credential.js +3 -12
- package/lib/create-server.js +47 -4
- package/lib/credentials.js +106 -0
- package/lib/git-helpers.js +115 -52
- package/lib/http-client.js +19 -6
- package/lib/instructions.generated.js +14 -0
- package/lib/instructions.js +37 -0
- package/lib/oauth-config.js +98 -0
- package/lib/oauth.js +353 -0
- package/lib/remote-tools.js +6 -0
- package/lib/token-store.js +136 -0
- package/lib/user-paths.js +41 -0
- package/lib/version.js +1 -1
- package/package.json +9 -6
- package/prompts/commands.generated.js +52 -0
- package/prompts/index.js +62 -29
- package/scripts/build-instructions.mjs +76 -0
- package/scripts/build-prompts.mjs +144 -0
- package/tools/auth.js +34 -0
- package/tools/index.js +4 -0
- package/prompts/ai-workflow-automation.js +0 -96
- package/prompts/zephly-usage-guide.js +0 -119
package/prompts/index.js
CHANGED
|
@@ -1,39 +1,72 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* MCP
|
|
3
|
-
*
|
|
2
|
+
* MCP prompts — the things a USER deliberately invokes (#2634).
|
|
3
|
+
*
|
|
4
|
+
* The split that decides what belongs here, now that the server also serves
|
|
5
|
+
* `instructions` (#2633):
|
|
6
|
+
*
|
|
7
|
+
* instructions = what the agent should ALWAYS do. Injected into system
|
|
8
|
+
* context on every session, so it must be short and it must
|
|
9
|
+
* not need asking for.
|
|
10
|
+
* prompts = actions a person chooses. Clients surface them as slash
|
|
11
|
+
* commands, so they are invoked, not absorbed.
|
|
12
|
+
*
|
|
13
|
+
* Serving the always-on discipline in both places would be the same duplicate
|
|
14
|
+
* that #2594 removed from every repo's CLAUDE.md, so it lives in exactly one.
|
|
15
|
+
*
|
|
16
|
+
* WHAT WAS REMOVED HERE, and why it is a fix rather than a loss. This module
|
|
17
|
+
* used to serve `zephly-usage-guide` and `ai-workflow-automation`. Both were
|
|
18
|
+
* pre-rebrand in the user-visible prompt NAME, and both instructed agents to
|
|
19
|
+
* open a session with `list_organizations()` and `list_projects()` — tools that
|
|
20
|
+
* no longer exist in TOOLS. A prompt that names missing tools is not stale
|
|
21
|
+
* documentation, it is an instruction to make a call that fails, and the
|
|
22
|
+
* work-tracking content that replaced it now ships as `instructions`.
|
|
4
23
|
*/
|
|
5
24
|
|
|
6
|
-
|
|
7
|
-
import { ZEPHLY_USAGE_GUIDE } from './zephly-usage-guide.js';
|
|
8
|
-
import { AI_WORKFLOW_AUTOMATION } from './ai-workflow-automation.js';
|
|
25
|
+
import { COMMAND_PROMPTS } from './commands.generated.js';
|
|
9
26
|
|
|
10
27
|
/**
|
|
11
|
-
*
|
|
28
|
+
* The placeholder a Claude Code command uses for its argument. Kept identical
|
|
29
|
+
* so one body serves both surfaces without a second copy.
|
|
12
30
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
const ARGUMENTS_TOKEN = '$ARGUMENTS';
|
|
32
|
+
|
|
33
|
+
/** Prompts available on a surface, in the MCP list shape. */
|
|
34
|
+
export function listPrompts(surface = 'local') {
|
|
35
|
+
return COMMAND_PROMPTS.filter((prompt) => prompt.surfaces.includes(surface)).map(
|
|
36
|
+
({ name, description, argumentHint }) => ({
|
|
37
|
+
name,
|
|
38
|
+
description,
|
|
39
|
+
arguments: argumentHint
|
|
40
|
+
? [
|
|
41
|
+
{
|
|
42
|
+
name: 'arguments',
|
|
43
|
+
description: argumentHint.replace(/^[[<]|[\]>]$/g, ''),
|
|
44
|
+
// Never required. `submit` and `untracked` are perfectly usable
|
|
45
|
+
// with nothing to add, and a required argument would make a
|
|
46
|
+
// client refuse to run them at all.
|
|
47
|
+
required: false,
|
|
48
|
+
},
|
|
49
|
+
]
|
|
50
|
+
: [],
|
|
51
|
+
})
|
|
52
|
+
);
|
|
53
|
+
}
|
|
24
54
|
|
|
25
55
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
56
|
+
* The body of one prompt, with the user's argument substituted.
|
|
57
|
+
*
|
|
58
|
+
* Returns null for a name this surface does not serve — including one it holds
|
|
59
|
+
* but does not serve remotely, so `submit` is as absent over the connector as
|
|
60
|
+
* a name that never existed. Reporting it differently would tell a caller a
|
|
61
|
+
* prompt is there and then refuse it.
|
|
29
62
|
*/
|
|
30
|
-
export function getPromptContent(name) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
default:
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
63
|
+
export function getPromptContent(name, args = {}, surface = 'local') {
|
|
64
|
+
const prompt = COMMAND_PROMPTS.find((candidate) => candidate.name === name);
|
|
65
|
+
if (!prompt || !prompt.surfaces.includes(surface)) return null;
|
|
66
|
+
|
|
67
|
+
const supplied = args?.arguments ?? '';
|
|
68
|
+
return prompt.body.split(ARGUMENTS_TOKEN).join(supplied);
|
|
39
69
|
}
|
|
70
|
+
|
|
71
|
+
/** Back-compat for callers that only want the list. */
|
|
72
|
+
export const PROMPTS = listPrompts('local');
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate lib/instructions.generated.js from the work-tracking skill.
|
|
4
|
+
*
|
|
5
|
+
* WHY GENERATE RATHER THAN WRITE IT TWICE. The MCP `instructions` string and
|
|
6
|
+
* plugins/ezmodo/skills/work-tracking/SKILL.md say the same thing to different
|
|
7
|
+
* audiences: the string reaches every MCP client on every session, the skill
|
|
8
|
+
* reaches Claude Code on demand and in full. Two hand-maintained prose copies
|
|
9
|
+
* of one contract is precisely the failure that moved these rules out of every
|
|
10
|
+
* repo's CLAUDE.md and into the plugin in the first place (#2594) — copies
|
|
11
|
+
* drift, and the drift is invisible until someone follows the stale one.
|
|
12
|
+
*
|
|
13
|
+
* WHY GENERATE AT BUILD TIME RATHER THAN READ THE SKILL AT RUNTIME. This
|
|
14
|
+
* package is published to npm and launched with `npx @ezmodo/mcp-server`. It
|
|
15
|
+
* ships no plugins/ directory and cannot: the skill lives in the plugin, which
|
|
16
|
+
* is a different artifact. So the extracted text is committed here, and
|
|
17
|
+
* __tests__/instructions.test.js re-extracts it whenever the skill IS present
|
|
18
|
+
* (i.e. in the repo, never in a published install) and fails on any difference.
|
|
19
|
+
* That test is the drift guard; there is no CI wiring to forget.
|
|
20
|
+
*
|
|
21
|
+
* Run: npm run generate:instructions --workspace=@ezmodo/mcp-server
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { readFileSync, writeFileSync } from 'fs';
|
|
25
|
+
import { dirname, join } from 'path';
|
|
26
|
+
import { fileURLToPath } from 'url';
|
|
27
|
+
|
|
28
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
export const SKILL_PATH = join(here, '..', '..', 'plugins', 'ezmodo', 'skills', 'work-tracking', 'SKILL.md');
|
|
30
|
+
const OUTPUT_PATH = join(here, '..', 'lib', 'instructions.generated.js');
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Pull the text between a marker pair.
|
|
34
|
+
*
|
|
35
|
+
* Throws rather than returning empty on a missing marker: a silently empty
|
|
36
|
+
* instructions string would ship a server that says nothing, and look fine.
|
|
37
|
+
*/
|
|
38
|
+
export function extract(markdown, marker) {
|
|
39
|
+
const open = `<!-- mcp:${marker}:start -->`;
|
|
40
|
+
const close = `<!-- mcp:${marker}:end -->`;
|
|
41
|
+
const from = markdown.indexOf(open);
|
|
42
|
+
const to = markdown.indexOf(close);
|
|
43
|
+
if (from === -1 || to === -1 || to < from) {
|
|
44
|
+
throw new Error(`Marker mcp:${marker} not found (or inverted) in the skill.`);
|
|
45
|
+
}
|
|
46
|
+
return markdown.slice(from + open.length, to).trim();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Build the module source from a skill document. */
|
|
50
|
+
export function render(markdown) {
|
|
51
|
+
const core = extract(markdown, 'core');
|
|
52
|
+
const local = extract(markdown, 'local');
|
|
53
|
+
|
|
54
|
+
return `/**
|
|
55
|
+
* GENERATED FILE — DO NOT EDIT.
|
|
56
|
+
*
|
|
57
|
+
* Extracted from plugins/ezmodo/skills/work-tracking/SKILL.md by
|
|
58
|
+
* scripts/build-instructions.mjs. Edit the SKILL, then run:
|
|
59
|
+
*
|
|
60
|
+
* npm run generate:instructions --workspace=@ezmodo/mcp-server
|
|
61
|
+
*
|
|
62
|
+
* __tests__/instructions.test.js fails if this drifts from the skill.
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
export const WORK_TRACKING_CORE = ${JSON.stringify(core)};
|
|
66
|
+
|
|
67
|
+
export const WORK_TRACKING_LOCAL = ${JSON.stringify(local)};
|
|
68
|
+
`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Only write when run directly, so the test can import the helpers above.
|
|
72
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
73
|
+
const skill = readFileSync(SKILL_PATH, 'utf-8');
|
|
74
|
+
writeFileSync(OUTPUT_PATH, render(skill), 'utf-8');
|
|
75
|
+
console.log(`Wrote ${OUTPUT_PATH}`);
|
|
76
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate prompts/commands.generated.js from the plugin's slash commands.
|
|
4
|
+
*
|
|
5
|
+
* WHAT PROMPTS ARE FOR, now that #2633 exists. The two overlap enough to be
|
|
6
|
+
* worth stating: `instructions` is what the agent should ALWAYS do, injected
|
|
7
|
+
* into system context on every session. Prompts are things a USER deliberately
|
|
8
|
+
* invokes. That split is what decides the content — the four plugin commands
|
|
9
|
+
* (start, resume, submit, untracked) are user-invoked actions, so they belong
|
|
10
|
+
* here; the always-on discipline does not, and serving it twice would be the
|
|
11
|
+
* drift problem again.
|
|
12
|
+
*
|
|
13
|
+
* Same generate-don't-retype rule as scripts/build-instructions.mjs, for the
|
|
14
|
+
* same reason: plugins/ is a different artifact and is absent from a published
|
|
15
|
+
* npx install, so the text is extracted at build time and committed, with
|
|
16
|
+
* __tests__/prompts.test.js re-extracting whenever the commands ARE present.
|
|
17
|
+
*
|
|
18
|
+
* TWO TRANSLATIONS ARE NECESSARY, neither cosmetic. Both are cases where the
|
|
19
|
+
* command body refers to something only Claude Code has, which an MCP client
|
|
20
|
+
* would read as an instruction it cannot carry out:
|
|
21
|
+
*
|
|
22
|
+
* 1. !`cmd` shell interpolation. The Claude Code CLI runs it before the model
|
|
23
|
+
* sees it. MCP has no such preprocessor, so the raw syntax arrives as
|
|
24
|
+
* literal text and the model reads a backtick as a command result.
|
|
25
|
+
* Rewritten into an explicit instruction to run the command.
|
|
26
|
+
* 2. "the **EzModo Work Tracking** skill". There are no skills outside Claude
|
|
27
|
+
* Code — but the same contract IS served to every client as the MCP
|
|
28
|
+
* `instructions` string (#2633), so the reference is repointed there
|
|
29
|
+
* rather than deleted. A reference to a sibling skill with no MCP
|
|
30
|
+
* equivalent (Link Upkeep) is dropped instead: the step that cites it
|
|
31
|
+
* already names the tools it needs, and pointing at something absent is
|
|
32
|
+
* worse than not pointing.
|
|
33
|
+
*
|
|
34
|
+
* Tests assert that neither form survives into a served prompt.
|
|
35
|
+
*
|
|
36
|
+
* Run: npm run generate:prompts --workspace=@ezmodo/mcp-server
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
40
|
+
import { dirname, join } from 'path';
|
|
41
|
+
import { fileURLToPath } from 'url';
|
|
42
|
+
|
|
43
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
44
|
+
export const COMMANDS_DIR = join(here, '..', '..', 'plugins', 'ezmodo', 'commands');
|
|
45
|
+
const OUTPUT_PATH = join(here, '..', 'prompts', 'commands.generated.js');
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Which surfaces each command is served on.
|
|
49
|
+
*
|
|
50
|
+
* `submit` is local-only: it reads git SHAs and links commits, neither of which
|
|
51
|
+
* exists on a hosted server. Serving it remotely would hand someone a checklist
|
|
52
|
+
* whose third step cannot be done.
|
|
53
|
+
*/
|
|
54
|
+
export const COMMANDS = [
|
|
55
|
+
{ file: 'start.md', name: 'start', surfaces: ['local', 'remote'] },
|
|
56
|
+
{ file: 'resume.md', name: 'resume', surfaces: ['local', 'remote'] },
|
|
57
|
+
{ file: 'submit.md', name: 'submit', surfaces: ['local'] },
|
|
58
|
+
{ file: 'untracked.md', name: 'untracked', surfaces: ['local', 'remote'] },
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
/** Split YAML-ish frontmatter from the body. Only the two keys we use. */
|
|
62
|
+
export function parseCommand(markdown) {
|
|
63
|
+
const match = /^---\n([\s\S]*?)\n---\n?/.exec(markdown);
|
|
64
|
+
if (!match) {
|
|
65
|
+
return { description: '', argumentHint: '', body: markdown.trim() };
|
|
66
|
+
}
|
|
67
|
+
const meta = {};
|
|
68
|
+
for (const line of match[1].split('\n')) {
|
|
69
|
+
const at = line.indexOf(':');
|
|
70
|
+
if (at === -1) continue;
|
|
71
|
+
meta[line.slice(0, at).trim()] = line.slice(at + 1).trim();
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
description: meta.description || '',
|
|
75
|
+
argumentHint: meta['argument-hint'] || '',
|
|
76
|
+
body: markdown.slice(match[0].length).trim(),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Rewrite Claude Code's !`cmd` shell interpolation into a plain instruction.
|
|
82
|
+
*
|
|
83
|
+
* `HEAD: !`git rev-parse HEAD`` becomes
|
|
84
|
+
* `HEAD: run `git rev-parse HEAD` and use its output`.
|
|
85
|
+
*/
|
|
86
|
+
export function stripShellInterpolation(body) {
|
|
87
|
+
return body.replace(/!`([^`]+)`/g, (_, command) => `run \`${command}\` and use its output`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Repoint or remove references to Claude Code skills.
|
|
92
|
+
*
|
|
93
|
+
* The patterns allow \s+ between words rather than a literal space: these
|
|
94
|
+
* bodies are hard-wrapped markdown, so a reference routinely straddles a line
|
|
95
|
+
* break and a space-literal pattern silently misses exactly those.
|
|
96
|
+
*
|
|
97
|
+
* Work Tracking is repointed because its content genuinely reaches every
|
|
98
|
+
* client, as this server's `instructions`. Anything else is removed with its
|
|
99
|
+
* sentence, because it does not.
|
|
100
|
+
*/
|
|
101
|
+
export function retargetSkillReferences(body) {
|
|
102
|
+
return body
|
|
103
|
+
.replace(
|
|
104
|
+
/the\s+\*\*EzModo\s+Work\s+Tracking\*\*\s+skill/g,
|
|
105
|
+
'the work-tracking contract in this server\'s instructions'
|
|
106
|
+
)
|
|
107
|
+
.replace(/\s*See\s+the\s+\*\*EzModo\s+[^*]+\*\*\s+skill\./g, '');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function render(read = (file) => readFileSync(join(COMMANDS_DIR, file), 'utf-8')) {
|
|
111
|
+
const entries = COMMANDS.map(({ file, name, surfaces }) => {
|
|
112
|
+
const { description, argumentHint, body } = parseCommand(read(file));
|
|
113
|
+
if (!description) throw new Error(`${file} has no description in its frontmatter.`);
|
|
114
|
+
return {
|
|
115
|
+
name,
|
|
116
|
+
description,
|
|
117
|
+
argumentHint,
|
|
118
|
+
surfaces,
|
|
119
|
+
body: retargetSkillReferences(stripShellInterpolation(body)),
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return `/**
|
|
124
|
+
* GENERATED FILE — DO NOT EDIT.
|
|
125
|
+
*
|
|
126
|
+
* Extracted from plugins/ezmodo/commands/ by scripts/build-prompts.mjs.
|
|
127
|
+
* Edit the command, then run:
|
|
128
|
+
*
|
|
129
|
+
* npm run generate:prompts --workspace=@ezmodo/mcp-server
|
|
130
|
+
*
|
|
131
|
+
* __tests__/prompts.test.js fails if this drifts from the commands.
|
|
132
|
+
*/
|
|
133
|
+
|
|
134
|
+
export const COMMAND_PROMPTS = ${JSON.stringify(entries, null, 2)};
|
|
135
|
+
`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1]) {
|
|
139
|
+
if (!existsSync(COMMANDS_DIR)) {
|
|
140
|
+
throw new Error(`No commands directory at ${COMMANDS_DIR} — run this from the repo.`);
|
|
141
|
+
}
|
|
142
|
+
writeFileSync(OUTPUT_PATH, render(), 'utf-8');
|
|
143
|
+
console.log(`Wrote ${OUTPUT_PATH}`);
|
|
144
|
+
}
|
package/tools/auth.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sign-in tools for the LOCAL (stdio) server (#2632).
|
|
3
|
+
*
|
|
4
|
+
* Never served remotely: over the connector, Claude performs its own OAuth
|
|
5
|
+
* before a single tool call is made, so an `authenticate` tool there would be
|
|
6
|
+
* an inert second sign-in offering to confuse people with. See
|
|
7
|
+
* lib/remote-tools.js.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const AUTH_TOOLS = [
|
|
11
|
+
{
|
|
12
|
+
name: 'authenticate',
|
|
13
|
+
description:
|
|
14
|
+
'Sign this EzModo MCP server in, or report who it is signed in as. Call ' +
|
|
15
|
+
'it when a tool reports that no credential is available. `login` ' +
|
|
16
|
+
'returns a URL to open in a browser — SHOW THAT URL TO THE USER, then ' +
|
|
17
|
+
'retry the original call once they say they have approved it. Not ' +
|
|
18
|
+
'needed when EZMODO_API_KEY is set.',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
action: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
enum: ['login', 'status', 'sign_out'],
|
|
25
|
+
description:
|
|
26
|
+
'login: start browser sign-in and return the URL to open (default). ' +
|
|
27
|
+
'status: report the current credential without changing anything. ' +
|
|
28
|
+
'sign_out: forget the stored tokens. Does not affect EZMODO_API_KEY ' +
|
|
29
|
+
'or the ezmodo CLI login, neither of which this server owns.',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
];
|
package/tools/index.js
CHANGED
|
@@ -40,8 +40,12 @@ import { FACT_TOOLS } from './facts.js';
|
|
|
40
40
|
import { AGENT_TOOLS } from './agents.js';
|
|
41
41
|
import { RECURRING_TASK_TOOLS } from './recurring-tasks.js';
|
|
42
42
|
import { WORK_TEMPLATE_TOOLS } from './work-templates.js';
|
|
43
|
+
import { AUTH_TOOLS } from './auth.js';
|
|
43
44
|
|
|
44
45
|
export const TOOLS = [
|
|
46
|
+
// First in the list on purpose: it is the one tool that works before the
|
|
47
|
+
// server has a credential, so it should be the one an agent notices.
|
|
48
|
+
...AUTH_TOOLS,
|
|
45
49
|
...ORGANIZATION_TOOLS,
|
|
46
50
|
...PROJECT_TOOLS,
|
|
47
51
|
...COMPONENT_TOOLS,
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AI Workflow Automation Prompt
|
|
3
|
-
* Guidelines for AI agents to automatically track their work in Zephly
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const AI_WORKFLOW_AUTOMATION = `# AI Workflow Automation Guide
|
|
7
|
-
|
|
8
|
-
## Overview
|
|
9
|
-
|
|
10
|
-
When you (as an AI agent) are implementing features or working on
|
|
11
|
-
complex tasks, **automatically track your work in Zephly in real-time**.
|
|
12
|
-
Don't wait for users to ask you to update documentation or check off tasks.
|
|
13
|
-
|
|
14
|
-
## When to Use Workflow Automation
|
|
15
|
-
|
|
16
|
-
- User asks you to implement a feature (not just explain or plan)
|
|
17
|
-
- Task involves multiple implementation steps (>3 steps)
|
|
18
|
-
- Work needs team visibility or historical tracking
|
|
19
|
-
|
|
20
|
-
## Session Setup
|
|
21
|
-
|
|
22
|
-
First, discover the user's context:
|
|
23
|
-
|
|
24
|
-
\`\`\`javascript
|
|
25
|
-
// 1. Get organization
|
|
26
|
-
const orgs = await list_organizations();
|
|
27
|
-
const orgId = orgs.organizations[0].id;
|
|
28
|
-
|
|
29
|
-
// 2. Get projects
|
|
30
|
-
const projects = await list_projects({ organizationId: orgId });
|
|
31
|
-
const projectId = projects.projects[0].id;
|
|
32
|
-
\`\`\`
|
|
33
|
-
|
|
34
|
-
## Workflow Steps
|
|
35
|
-
|
|
36
|
-
1. **Create or Select Project** - Use existing project or create new one with \`create_project\`
|
|
37
|
-
2. **Create Epic** (optional) - Use \`create_epic\` to group related tasks under a milestone
|
|
38
|
-
3. **Break Down Tasks** - Create 3-7 tasks with \`create_task\`, each with implementation steps
|
|
39
|
-
4. **Track Progress** - Update steps with \`update_task\` (toggle, add, modify) as you work
|
|
40
|
-
5. **Update Status** - Move tasks through workflow: backlog → todo → in_progress → in_review → completed
|
|
41
|
-
6. **Document** - Use \`create_document\` to create setup guides, API docs, or troubleshooting guides
|
|
42
|
-
7. **Complete** - Use \`complete_task\` when done, update epic status if applicable
|
|
43
|
-
|
|
44
|
-
## Example Workflow
|
|
45
|
-
|
|
46
|
-
\`\`\`javascript
|
|
47
|
-
// 1. Get context
|
|
48
|
-
const orgs = await list_organizations();
|
|
49
|
-
const orgId = orgs.organizations[0].id;
|
|
50
|
-
const projects = await list_projects({ organizationId: orgId });
|
|
51
|
-
const projectId = projects.projects[0].id;
|
|
52
|
-
|
|
53
|
-
// 2. Create epic (milestone)
|
|
54
|
-
const epic = await create_epic({
|
|
55
|
-
projectId,
|
|
56
|
-
title: "User Authentication System",
|
|
57
|
-
description: "Complete auth implementation with OAuth2"
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// 3. Create tasks with steps
|
|
61
|
-
await create_task({
|
|
62
|
-
projectId,
|
|
63
|
-
epicId: epic.id,
|
|
64
|
-
title: "Implement OAuth2 backend",
|
|
65
|
-
description: "Set up OAuth2 authentication flow",
|
|
66
|
-
steps: ["Set up OAuth2 config", "JWT generation", "Validation middleware", "Tests"],
|
|
67
|
-
assigneeType: "ai",
|
|
68
|
-
assigneeId: "claude",
|
|
69
|
-
assigneeName: "Claude"
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// 4. Track progress as you work
|
|
73
|
-
await update_task({ taskId: "task1", status: "in_progress" });
|
|
74
|
-
await update_task({ taskId: "task1", toggleStep: { stepId: "step_0", completed: true } });
|
|
75
|
-
|
|
76
|
-
// 5. Document what you built
|
|
77
|
-
await create_document({
|
|
78
|
-
projectId,
|
|
79
|
-
title: "Authentication Setup Guide",
|
|
80
|
-
content: "# How to configure OAuth2...",
|
|
81
|
-
type: "setup"
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
// 6. Complete task
|
|
85
|
-
await complete_task({ taskId: "task1", completionNotes: "OAuth2 implementation complete" });
|
|
86
|
-
|
|
87
|
-
// 7. Update epic when all tasks done
|
|
88
|
-
await update_epic({ epicId: epic.id, status: "completed" });
|
|
89
|
-
\`\`\`
|
|
90
|
-
|
|
91
|
-
## Benefits
|
|
92
|
-
|
|
93
|
-
- Real-time visibility for users (no "black box" AI work)
|
|
94
|
-
- Historical record of decisions and implementation details
|
|
95
|
-
- Documentation created alongside code
|
|
96
|
-
- The only PM tool where AI agents manage their own project tracking`;
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zephly Usage Guide Prompt
|
|
3
|
-
* Guidelines for using Zephly MCP tools effectively
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export const ZEPHLY_USAGE_GUIDE = `# Zephly MCP Integration Guide
|
|
7
|
-
|
|
8
|
-
You have access to Zephly's intelligent project management tools.
|
|
9
|
-
Zephly uses a **project-first** model where projects are the primary
|
|
10
|
-
work containers.
|
|
11
|
-
|
|
12
|
-
## Session Start - Discover Context
|
|
13
|
-
|
|
14
|
-
At the start of each session, discover the user's context:
|
|
15
|
-
|
|
16
|
-
\`\`\`
|
|
17
|
-
1. list_organizations() # Get user's workspaces (usually one)
|
|
18
|
-
2. list_projects({ organizationId }) # Get projects
|
|
19
|
-
\`\`\`
|
|
20
|
-
|
|
21
|
-
This gives you the organizationId and projects needed for all other operations.
|
|
22
|
-
|
|
23
|
-
## Project-First Hierarchy
|
|
24
|
-
|
|
25
|
-
\`\`\`
|
|
26
|
-
Organization
|
|
27
|
-
└── Projects (primary work containers)
|
|
28
|
-
└── Epics (milestones within projects)
|
|
29
|
-
└── Tasks (individual work items)
|
|
30
|
-
└── Goals (strategic alignment, links to epics)
|
|
31
|
-
\`\`\`
|
|
32
|
-
|
|
33
|
-
**Key points:**
|
|
34
|
-
- Tasks belong to **projects** (projectId is required)
|
|
35
|
-
- Epics are project-scoped milestones
|
|
36
|
-
- Goals provide strategic alignment and can link to epics
|
|
37
|
-
|
|
38
|
-
## When to Use Zephly Tools
|
|
39
|
-
|
|
40
|
-
**Proactively use these tools when:**
|
|
41
|
-
- User mentions tasks, projects, epics, sprints, or team coordination
|
|
42
|
-
- User asks about project status, health, or blockers
|
|
43
|
-
- User needs help prioritizing work or deciding what to work on next
|
|
44
|
-
- User is planning team assignments or workload distribution
|
|
45
|
-
- User is breaking down features into tasks
|
|
46
|
-
- User asks about timelines or estimates
|
|
47
|
-
- User mentions dependencies, blockers, or handoffs
|
|
48
|
-
|
|
49
|
-
## Core Tools
|
|
50
|
-
|
|
51
|
-
### Discovery
|
|
52
|
-
- \`list_organizations\` - List all workspaces
|
|
53
|
-
- \`list_projects\` - List projects for an organization
|
|
54
|
-
- \`search_projects\` - Search/filter projects
|
|
55
|
-
|
|
56
|
-
### Project Management
|
|
57
|
-
- \`create_project\` - Create new project
|
|
58
|
-
- \`get_project_context\` - Get project details with progress
|
|
59
|
-
|
|
60
|
-
### Task Operations
|
|
61
|
-
- \`create_task\` - Create task in a project (requires projectId)
|
|
62
|
-
- \`update_task\` - Update task properties and steps
|
|
63
|
-
- \`complete_task\` - Mark task completed
|
|
64
|
-
- \`search_tasks\` - Semantic search or filter tasks
|
|
65
|
-
- \`get_task\` - Get task by ID or number
|
|
66
|
-
|
|
67
|
-
### Epic Management
|
|
68
|
-
- \`create_epic\` - Create epic in project
|
|
69
|
-
- \`update_epic\` - Update epic
|
|
70
|
-
- \`list_epics\` - List epics for project
|
|
71
|
-
- \`get_epic\` - Get epic details
|
|
72
|
-
|
|
73
|
-
### Goal Management
|
|
74
|
-
- \`create_goal\` - Create strategic goal
|
|
75
|
-
- \`update_goal\` - Update goal
|
|
76
|
-
- \`list_goals\` - List goals for organization
|
|
77
|
-
- \`link_epic_to_goal\` - Link epic to goal for alignment
|
|
78
|
-
|
|
79
|
-
### AI Intelligence
|
|
80
|
-
- \`get_project_insights\` - Project health analysis
|
|
81
|
-
- \`suggest_next_actions\` - Task recommendations
|
|
82
|
-
- \`analyze_dependency_graph\` - Critical path analysis
|
|
83
|
-
- \`estimate_task_duration\` - AI time estimates
|
|
84
|
-
|
|
85
|
-
### Team Coordination
|
|
86
|
-
- \`analyze_team_capacity\` - Resource allocation
|
|
87
|
-
- \`suggest_task_assignments\` - Smart task matching
|
|
88
|
-
- \`bulk_task_operations\` - Batch operations
|
|
89
|
-
- \`identify_handoff_needs\` - Detect blocked tasks
|
|
90
|
-
- \`pull_ai_task_from_queue\` - AI agent task queue
|
|
91
|
-
|
|
92
|
-
## Example Workflow
|
|
93
|
-
|
|
94
|
-
\`\`\`javascript
|
|
95
|
-
// 1. Discover context
|
|
96
|
-
const orgs = await list_organizations();
|
|
97
|
-
const orgId = orgs.organizations[0].id;
|
|
98
|
-
|
|
99
|
-
const projects = await list_projects({ organizationId: orgId });
|
|
100
|
-
const projectId = projects.projects[0].id;
|
|
101
|
-
|
|
102
|
-
// 2. Create a task
|
|
103
|
-
await create_task({
|
|
104
|
-
projectId,
|
|
105
|
-
title: 'Implement user authentication',
|
|
106
|
-
description: 'Add login/logout functionality',
|
|
107
|
-
priority: 'high'
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
// 3. Search existing tasks
|
|
111
|
-
await search_tasks({ projectId, status: 'in_progress' });
|
|
112
|
-
\`\`\`
|
|
113
|
-
|
|
114
|
-
## Best Practices
|
|
115
|
-
|
|
116
|
-
1. **Discover context first** - Call list_organizations + list_projects at session start
|
|
117
|
-
2. **Project-first** - Always have a projectId before creating tasks
|
|
118
|
-
3. **Check health proactively** - Use get_project_insights to identify issues
|
|
119
|
-
4. **Track AI work** - Create tasks for significant work you're doing`;
|