modelcontextprotocol / modelcontextprotocol/typescript-sdk
Feature request: allow customizing `taskId` in `InMemoryTaskStore`
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 13.4k
- Forks
- 2.2k
- Avg merge
- 3d 15h
- Merged PRs (30d)
- 4
Description
Allow customizing taskId in InMemoryTaskStore
Is your feature request related to a problem? Please describe.
InMemoryTaskStore.createTask(taskParams, requestId, …) accepts a requestId but does not use it as the taskId. It generates its own opaque 32-char hex ID via the private generateTaskId(), and there is no extension point.
In the Apify MCP server we would like to use short, deterministic task IDs in stdio mode. Apify uses mcpc for testing and would like more descriptive IDs:
# current
{ "taskId": "e6f8c1a4d3b2497f8a1c5d3e7b9f0a2c", "status": "working" }
# desired
{ "taskId": "apify--rag-web-browser-LUiwRAkz", "status": "working" }
The desired form tells the reader (and LLM) the originating tool at a glance.
InMemoryTaskStore is documented as "for development and testing", but stdio is inherently in-memory — reimplementing TaskStore from scratch just to customize the ID strategy means maintaining a parallel copy of logic the SDK already provides. The hosted server at mcp.apify.com already runs a Redis-backed TaskStore, so the in-memory case is genuinely the right fit for stdio.
Describe the solution you'd like
Two approaches — either solves it. Listed in order of how invasive they are.
Option A — make generateTaskId protected (one-token diff)
// before
private generateTaskId(): string { … }
// after
protected generateTaskId(): string { … }
The subclass stashes the desired ID in createTask and returns it from generateTaskId:
class ApifyInMemoryTaskStore extends InMemoryTaskStore {
private nextId?: string;
override async createTask(p, requestId, req, sid?) {
this.nextId = String(requestId);
return super.createTask(p, requestId, req, sid);
}
protected override generateTaskId() {
return this.nextId ?? super.generateTaskId();
}
}
Pros: one narrow extension point. Everything else (tasks map, TTL timer, getStoredTask session isolation, listTasks pagination) stays SDK-owned and keeps evolving transparently for subclassers. Zero behavior change for current users.
Option B — add taskId to CreateTaskOptions
interface CreateTaskOptions {
ttl?: number;
pollInterval?: number;
taskId?: string; // caller-supplied; falls back to store-generated
}
In each store impl:
const taskId = taskParams.taskId ?? this.generateTaskId();
Caller side becomes a one-liner, no subclass needed:
taskStore.createTask({ ttl, taskId: 'apify--rag-web-browser-LUiwRAkz' }, request.id, request);
Pros: the "I want a specific taskId" intent is explicit in the API instead of smuggled through requestId. The same hook also works for any future SDK consumer wanting tracing-friendly, sortable, or domain-prefixed IDs.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading InMemoryTaskStore.createTask, its private generateTaskId method, and CreateTaskOptions, then compare the other store implementations. Determine whether the supported extension should be a protected generator or an explicit taskId option, while preserving generated IDs for existing callers; done means the chosen API supports custom IDs without changing current behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- api, backend
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100