@@ -369,11 +369,11 @@ def _format_constrained_text(constrained_project: str) -> str:
369369 return result
370370
371371
372- async def _resolve_create_project_workspace (
372+ async def _resolve_workspace_routing (
373373 workspace : str | None ,
374374 context : Context | None ,
375375) -> str | None :
376- """Resolve the create-project workspace selector to the routing tenant id."""
376+ """Resolve an optional workspace selector to the routing tenant id."""
377377 if workspace is None :
378378 return None
379379
@@ -389,7 +389,8 @@ async def _resolve_create_project_workspace(
389389 # a friendly selector such as a slug, name, or tenant id.
390390 # Why: MCP callers should not need to paste UUIDs, but the transport still
391391 # uses X-Workspace-ID with the tenant id as its routing authority.
392- # Outcome: resolve once at create time and pass only the tenant id downstream.
392+ # Outcome: resolve once before the project-management request and pass only
393+ # the tenant id downstream.
393394 resolved_workspace = await resolve_workspace_parameter (workspace = workspace , context = context )
394395 return resolved_workspace .tenant_id
395396
@@ -418,8 +419,8 @@ async def create_memory_project(
418419 workspace: Optional cloud workspace selector to create the project in. Slug is
419420 preferred for AI callers, but tenant_id and unique name are also accepted.
420421 When omitted, the connection's default workspace is used. Discover values
421- via `list_workspaces`. Only meaningful in cloud mode; ignored for local
422- projects .
422+ via `list_workspaces`. In local mode the selector is passed through
423+ without slug resolution .
423424 output_format: "text" returns the existing human-readable result text.
424425 "json" returns structured project creation metadata.
425426 context: Optional FastMCP context for progress/status logging.
@@ -432,31 +433,34 @@ async def create_memory_project(
432433 create_memory_project("work-notes", "/home/user/work", set_default=True)
433434 create_memory_project("team-notes", "/team/notes", workspace="team-paul")
434435 """
435- workspace_id = await _resolve_create_project_workspace (workspace , context )
436+ # Trigger: MCP server is constrained to a single project.
437+ # Why: constrained sessions cannot create projects, and workspace selectors
438+ # may be invalid or unavailable in that locked context.
439+ # Outcome: return the existing disabled response before opening a routed client.
440+ constrained_project = os .environ .get ("BASIC_MEMORY_MCP_PROJECT" )
441+ if constrained_project :
442+ if output_format == "json" :
443+ return {
444+ "name" : project_name ,
445+ "path" : project_path ,
446+ "is_default" : False ,
447+ "created" : False ,
448+ "already_exists" : False ,
449+ "error" : "PROJECT_CONSTRAINED" ,
450+ "message" : (
451+ f"Project creation disabled - MCP server is constrained to project "
452+ f"'{ constrained_project } '."
453+ ),
454+ }
455+ return f'# Error\n \n Project creation disabled - MCP server is constrained to project \' { constrained_project } \' .\n Use the CLI to create projects: `basic-memory project add "{ project_name } " "{ project_path } "`'
456+
457+ workspace_id = await _resolve_workspace_routing (workspace , context )
436458
437459 # workspace targets a non-default cloud workspace at create time.
438460 # Trigger: caller passed workspace (e.g. a slug discovered via list_workspaces).
439461 # Why: there is no project_id yet for per-project routing — the project doesn't exist.
440462 # Outcome: cloud factory routes the create request to the resolved workspace tenant id.
441463 async with get_client (workspace = workspace_id ) as client :
442- # Check if server is constrained to a specific project
443- constrained_project = os .environ .get ("BASIC_MEMORY_MCP_PROJECT" )
444- if constrained_project :
445- if output_format == "json" :
446- return {
447- "name" : project_name ,
448- "path" : project_path ,
449- "is_default" : False ,
450- "created" : False ,
451- "already_exists" : False ,
452- "error" : "PROJECT_CONSTRAINED" ,
453- "message" : (
454- f"Project creation disabled - MCP server is constrained to project "
455- f"'{ constrained_project } '."
456- ),
457- }
458- return f'# Error\n \n Project creation disabled - MCP server is constrained to project \' { constrained_project } \' .\n Use the CLI to create projects: `basic-memory project add "{ project_name } " "{ project_path } "`'
459-
460464 if context : # pragma: no cover
461465 await context .info (f"Creating project: { project_name } at { project_path } " )
462466
@@ -536,7 +540,11 @@ async def create_memory_project(
536540@mcp .tool (
537541 annotations = {"destructiveHint" : True , "openWorldHint" : False },
538542)
539- async def delete_project (project_name : str , context : Context | None = None ) -> str :
543+ async def delete_project (
544+ project_name : str ,
545+ workspace : str | None = None ,
546+ context : Context | None = None ,
547+ ) -> str :
540548 """Delete a Basic Memory project.
541549
542550 Removes a project from the configuration and database. This does NOT delete
@@ -545,23 +553,33 @@ async def delete_project(project_name: str, context: Context | None = None) -> s
545553
546554 Args:
547555 project_name: Name of the project to delete
556+ workspace: Optional cloud workspace selector to delete the project from.
557+ Slug is preferred for AI callers, but tenant_id and unique name are
558+ also accepted. When omitted, the connection's default workspace is
559+ used. In local mode the selector is passed through without slug
560+ resolution, matching create_memory_project behavior.
548561
549562 Returns:
550563 Confirmation message about project deletion
551564
552565 Example:
553566 delete_project("old-project")
567+ delete_project("team-project", workspace="team-paul")
554568
555569 Warning:
556570 This action cannot be undone. The project will need to be re-added
557571 to access its content through Basic Memory again.
558572 """
559- async with get_client () as client :
560- # Check if server is constrained to a specific project
561- constrained_project = os .environ .get ("BASIC_MEMORY_MCP_PROJECT" )
562- if constrained_project :
563- return f"# Error\n \n Project deletion disabled - MCP server is constrained to project '{ constrained_project } '.\n Use the CLI to delete projects: `basic-memory project remove \" { project_name } \" `"
573+ # Trigger: MCP server is constrained to a single project.
574+ # Why: constrained sessions cannot delete projects, and workspace selectors
575+ # may be invalid or unavailable in that locked context.
576+ # Outcome: return the existing disabled message before opening a routed client.
577+ constrained_project = os .environ .get ("BASIC_MEMORY_MCP_PROJECT" )
578+ if constrained_project :
579+ return f"# Error\n \n Project deletion disabled - MCP server is constrained to project '{ constrained_project } '.\n Use the CLI to delete projects: `basic-memory project remove \" { project_name } \" `"
564580
581+ workspace_id = await _resolve_workspace_routing (workspace , context )
582+ async with get_client (workspace = workspace_id ) as client :
565583 if context : # pragma: no cover
566584 await context .info (f"Deleting project: { project_name } " )
567585
0 commit comments