Skip to content

Commit ab2adee

Browse files
authored
Merge pull request #15 from thomasleveil/feat/read_only_mode
Feat/read only mode
2 parents f13d732 + cce9d03 commit ab2adee

File tree

3 files changed

+354
-297
lines changed

3 files changed

+354
-297
lines changed

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@ More details on MCP here:
1313

1414
<a href="https://glama.ai/mcp/servers/ln1qzdhwmc"><img width="380" height="200" src="https://glama.ai/mcp/servers/ln1qzdhwmc/badge" alt="mcp-server-asana MCP server" /></a>
1515

16+
## Environment Variables
17+
18+
- `ASANA_ACCESS_TOKEN`: (Required) Your Asana access token
19+
- `READ_ONLY_MODE`: (Optional) Set to 'true' to disable all write operations. In this mode:
20+
- Tools that modify Asana data (create, update, delete) will be disabled
21+
- The `create-task` prompt will be disabled
22+
- Only read operations will be available
23+
This is useful for testing or when you want to ensure no changes can be made to your Asana workspace.
24+
1625
## Usage
1726

1827
In the AI tool of your choice (ex: Claude Desktop) ask something about asana tasks, projects, workspaces, and/or comments. Mentioning the word "asana" will increase the chance of having the LLM pick the right tool.

src/prompt-handler.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,18 +67,35 @@ const PROMPTS: Record<string, PromptDefinition> = {
6767
}
6868
};
6969

70+
// List of prompts that only read Asana state
71+
const READ_ONLY_PROMPTS = [
72+
'task-summary',
73+
'task-completeness'
74+
];
75+
76+
// Filter prompts based on READ_ONLY_MODE
77+
const isReadOnlyMode = process.env.READ_ONLY_MODE === 'true';
78+
7079
export function createPromptHandlers(asanaClient: AsanaClientWrapper): PromptHandler {
7180
return {
7281
listPrompts: async (): Promise<ListPromptsResult> => {
7382
console.error("Received ListPromptsRequest");
83+
const availablePrompts = isReadOnlyMode
84+
? Object.values(PROMPTS).filter(prompt => READ_ONLY_PROMPTS.includes(prompt.name))
85+
: Object.values(PROMPTS);
7486
return {
75-
prompts: Object.values(PROMPTS)
87+
prompts: availablePrompts
7688
};
7789
},
7890

7991
getPrompt: async (request: GetPromptRequest): Promise<GetPromptResult> => {
8092
console.error("Received GetPromptRequest:", request);
8193

94+
// Block non-read prompts in read-only mode
95+
if (isReadOnlyMode && !READ_ONLY_PROMPTS.includes(request.params.name)) {
96+
throw new Error(`Prompt ${request.params.name} is not available in read-only mode`);
97+
}
98+
8299
const prompt = PROMPTS[request.params.name];
83100
if (!prompt) {
84101
throw new Error(`Prompt not found: ${request.params.name}`);
@@ -114,13 +131,13 @@ Task Notes:
114131
${task.notes || "No notes"}
115132
116133
Custom Fields:
117-
${task.custom_fields?.map((field: {name: string; display_value: string}) =>
118-
`${field.name}: ${field.display_value}`).join('\n') || "No custom fields"}
134+
${task.custom_fields?.map((field: { name: string; display_value: string }) =>
135+
`${field.name}: ${field.display_value}`).join('\n') || "No custom fields"}
119136
120137
Comments/Updates (from newest to oldest):
121-
${stories.map((story: {created_at: string; text: string}) =>
122-
`[${new Date(story.created_at).toLocaleString()}] ${story.text}`
123-
).join('\n\n') || "No comments"}
138+
${stories.map((story: { created_at: string; text: string }) =>
139+
`[${new Date(story.created_at).toLocaleString()}] ${story.text}`
140+
).join('\n\n') || "No comments"}
124141
125142
Please include:
126143
1. Current status and progress
@@ -191,11 +208,11 @@ Task: ${taskId}
191208
const projectName = request.params.arguments?.project_name;
192209
const title = request.params.arguments?.title;
193210

194-
if (!projectName ) {
211+
if (!projectName) {
195212
throw new Error("Project ID is required");
196213
}
197214

198-
if (!title ) {
215+
if (!title) {
199216
throw new Error("Task title is required");
200217
}
201218

0 commit comments

Comments
 (0)