Starspace logoStarspace
    /Docs

    MCP Server

    Connect AI assistants like ChatGPT, Claude, and Cursor to your Starspace workspace using the Model Context Protocol.

    Base URL

    https://api.starspace.run/functions/v1/mcp-run-v1

    Two routes are available:

    RouteCapabilitiesUse case
    /Read + WriteFull access, IDE integrations, automation
    /readonly/Read onlyChatGPT connectors, read-only assistants
    Tip: When connecting ChatGPT, always use the /readonly/ route.
    Workspace flavor affects the tool surface. Indexed workspaces expose search and ask alongside the run/edit/fetch tools. Sandbox workspaces share the same virtual FS but have no semantic index, so search and ask are absent fromtools/list and direct calls return a friendly inline error pointing at run_readonly with rg/grep. Always inspect tools/list on session start - don't assume.

    Transport

    Streamable HTTP, the server accepts POST requests with JSON-RPC 2.0 payloads.

    Required headershttp
    Content-Type: application/json
    Accept: application/json, text/event-stream

    Both Accept values are required. Omit either and the server returns 406 not_acceptable. This is mandated by the MCP Streamable HTTP transport spec, since the server may stream the response.

    Supported protocol versions: 2025-11-25, 2025-03-26, 2024-11-05

    The server is stateless, each request creates a fresh instance. Session headers are accepted but not required.

    Response format

    Responses are framed as Server-Sent Events: each JSON-RPC envelope arrives as a data: <json>\n\n line. Even single-shot tool calls (no streaming) come back this way, so don't JSON.parse(body) directly — strip the data: prefix first.

    Minimal client parsejavascript
    // 1. Strip SSE framing
    const env = JSON.parse(
      body.split('\n')
        .filter(l => l.startsWith('data:'))
        .map(l => l.slice(5).trim())
        .filter(s => s && s !== '[DONE]')
        .pop()
    );
    
    // 2. Pull structured output directly (modern path — no second parse)
    const data = env.result.structuredContent;          // { results: [...] }
    
    // 3. Legacy path: same JSON, escaped as a string inside content[0].text
    const legacy = JSON.parse(env.result.content[0].text);

    For tools with an outputSchema (search, fetch, manifest, server_health, …), prefer result.structuredContent — it's a real JSON object so you skip the second parse. result.content[0].text carries the same payload as an escaped JSON string for older clients that don't honor structured output (MCP 2025-06-18 feature).

    Authentication

    1. API Key (simplest)

    Pass your workspace API key as a Bearer token or query parameter.

    Bearer headerbash
    curl -X POST \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
      https://api.starspace.run/functions/v1/mcp-run-v1
    Query parameterbash
    curl -X POST \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
      "https://api.starspace.run/functions/v1/mcp-run-v1?key=ak_your_key_here"

    Create API keys via the Workspace API.

    2. OAuth 2.0 (PKCE)

    For third-party integrations, the server supports OAuth 2.0 Authorization Code flow with PKCE (S256).

    EndpointDescription
    /.well-known/oauth-authorization-serverAuthorization server metadata
    /.well-known/oauth-protected-resourceProtected resource metadata
    /oauth/authorizeStart authorization (GET)
    /oauth/tokenExchange code for tokens (POST)
    /oauth/registerDynamic client registration (POST)
    /oauth/revokeRevoke a token (POST)

    Scopes

    ScopeDescription
    offline_accessRequest a refresh token
    workspace:readRead workspace files and search
    workspace:askExecute read-only commands
    workspace:writeModify files and execute write commands

    workspace:write implicitly grants workspace:read and workspace:ask.

    Tools

    POSTtools/call → search

    Hybrid full-text + trigram + symbol + chunk search with intent-aware LLM reranking. Returns ranked candidates with confidence scores and one-line rationales for why each result was picked.

    Parameters

    NameTypeRequiredDescription
    querystringSearch query
    limitinteger, Max results, 1–50 (default 10)
    path_prefixstring, Restrict to files under this directory
    file_globstring, Restrict to files matching this glob (e.g. '*.ts', '**/auth/*.py')
    modestring, exact | semantic | debug | auto (default auto)

    Scope: workspace:read or workspace:ask

    Example requestbash
    curl -X POST \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{
        "jsonrpc": "2.0", "id": 1,
        "method": "tools/call",
        "params": {
          "name": "search",
          "arguments": {
            "query": "openai compatible api server routes chat completions",
            "limit": 5
          }
        }
      }' \
      https://api.starspace.run/functions/v1/mcp-run-v1

    Response Shape

    The object below is what arrives on result.structuredContent after you strip SSE framing. The same JSON is also escaped inside result.content[0].text for older clients — see the Transport section for a minimal parse.

    {
      "results": [
        {
          "id": "file:src/api_router.py:L7-L42",
          "path": "src/api_router.py",
          "title": "api_router.py (L7-L42)",
          "url": "https://app.starspace.run/w/<id>/file/src/api_router.py",
          "text": "snippet ...",
          "score": 1.0,
          "score_raw": 0.0016,
          "confidence": "high",
          "rerank_rank": 1,
          "why": "Canonical FastAPI router for OpenAI chat completion routes",
          "match_type": "chunk",
          "start_line": 7,
          "end_line": 42,
          "matched_lines": [
            { "line": 12, "text": "@router.post('/v1/chat/completions')" }
          ]
        }
      ],
      "query_interpretation": {
        "mode_used": "semantic",
        "intent_detected": "semantic",
        "filters_applied": { "limit": 5 },
        "reranked": true,
        "rerank_reason": "applied"
      }
    }
    Result fieldDescription
    idStable citation. Pass back to fetch. Form: file:<path>:L<start>-L<end> when range is known, plain file:<path> otherwise.
    pathPlain file path, normalized (no line range suffix).
    score0–1 normalized for display. 1.0 = top hit in this set.
    score_rawOriginal RRF (Reciprocal Rank Fusion) float. For clients that need raw signal.
    confidencehigh (≥0.7) | medium (≥0.4) | low. Bucketed from score.
    rerank_rank1-indexed position from the LLM reranker (only present when rerank ran).
    whyOne-line rationale from the reranker (only present when rerank ran).
    match_typechunk | fts | path | symbol | content. Where the match came from.
    start_line / end_lineLine range coherent with id. Use as-is for fetch.
    matched_linesUp to 3 highest-scoring lines with absolute line numbers.
    query_interpretationDescription
    mode_usedEffective mode after override or auto-classification.
    intent_detectedWhat the auto-classifier picked from the query shape.
    filters_appliedEcho of path_prefix, file_glob, and limit actually used.
    rerankedtrue if at least one result carries a rerank_rank.
    rerank_reasonapplied | disabled | no_results | too_few_candidates | exact_mode | gemini_error.
    POSTtools/call → fetch

    Retrieve a document by its ID. Pass an id straight from search results - the embedded line range (file:<path>:L<s>-L<e>) is parsed automatically.

    Parameters

    NameTypeRequiredDescription
    idstringDocument ID from search (file:<path> or file:<path>:L<s>-L<e>)
    start_lineinteger, Override the embedded range (1-indexed)
    end_lineinteger, Override the embedded range (1-indexed). Capped at start + 2000 lines.

    Scope: workspace:read or workspace:ask

    Example requestbash
    curl -X POST \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{
        "jsonrpc": "2.0", "id": 1,
        "method": "tools/call",
        "params": {"name": "fetch", "arguments": {"id": "file:src/api_router.py:L7-L42"}}
      }' \
      https://api.starspace.run/functions/v1/mcp-run-v1
    POSTtools/call → server_health

    Returns server diagnostics. Use this to verify connectivity and check if the tool manifest has changed.

    Scope: none required

    FieldDescription
    instance_idUnique ID for the workspace MCP server instance handling this session
    server_versionServer version string
    toolset_hashSHA-256 hash of the tool manifest, stable across restarts
    uptime_msMilliseconds since boot
    statelessAlways true
    POSTtools/call → run_readonly

    Execute read-only Unix command chains against your workspace filesystem.

    Parameters

    NameTypeRequiredDescription
    commandstringCommand chain to execute

    Scope: workspace:read or workspace:ask

    Available commands: ls, tree, cat, head, tail, find, grep, rg, outline, diff, wc, sort, uniq, xargs, cd, pwd, echo, stat, lines, help

    curl -X POST \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{
        "jsonrpc": "2.0", "id": 1,
        "method": "tools/call",
        "params": {"name": "run_readonly", "arguments": {"command": "tree / | head 50"}}
      }' \
      https://api.starspace.run/functions/v1/mcp-run-v1
    POSTtools/call → run

    Execute command chains with read-only or read-write access depending on the route.

    Parameters

    NameTypeRequiredDescription
    commandstringCommand chain to execute

    On the default route: full read-write access. On /readonly/: same as run_readonly.

    Additional write-mode commands: write, rm, patch, fetch, note

    POSTtools/call → write_file

    Create or overwrite a file in the workspace.

    Parameters

    NameTypeRequiredDescription
    pathstringFile path relative to workspace root
    contentstringComplete file content

    Scope: workspace:write · Default route only

    POSTtools/call → apply_patch

    Apply a surgical edit to an existing file.

    Parameters

    NameTypeRequiredDescription
    pathstringFile path relative to workspace root
    patchstringPatch content in *** Begin Patch format

    Scope: workspace:write · Default route only

    Patch format examplediff
    *** Begin Patch
    *** Update File: src/main.ts
    @@@ -10,3 +10,5 @@@
     import { foo } from './foo';
    -import { bar } from './bar';
    +import { bar } from './bar';
    +import { baz } from './baz';
    +
     const app = createApp();
    *** End Patch

    Health Endpoints

    Simple health checks (GET, no auth required). Returns 200 OK with {"status":"ok"}.

    curl https://api.starspace.run/functions/v1/mcp-run-v1/health
    curl https://api.starspace.run/functions/v1/mcp-run-v1/readonly/health

    Rate Limiting

    120 requests per minute per workspace. Exceeding the limit returns 429 Too Many Requests.

    Connecting from AI Platforms

    ChatGPT

    1. Go to Settings → Connected Apps → Add MCP Server
    2. Enter the readonly URL:
    https://api.starspace.run/functions/v1/mcp-run-v1/readonly/?key=ak_your_key_here
    Known behavior: ChatGPT wraps tool URIs with link_<id> identifiers that may expire independently. If you get "Resource not found" errors, re-trigger tool discovery. Verify with server_health and compare toolset_hash.

    Claude

    Add a custom MCP server with your API key as a query parameter:

    https://api.starspace.run/functions/v1/mcp-run-v1?key=ak_your_key_here

    Cursor / IDEs

    Configure the MCP server in your IDE settings:

    MCP configurationjson
    {
      "mcpServers": {
        "starspace": {
          "url": "https://api.starspace.run/functions/v1/mcp-run-v1",
          "headers": {
            "Authorization": "Bearer ak_your_key_here"
          }
        }
      }
    }

    Direct HTTP

    List available toolsbash
    curl -X POST \
      -H "Authorization: Bearer ak_your_key_here" \
      -H "Content-Type: application/json" \
      -H "Accept: application/json, text/event-stream" \
      -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
      https://api.starspace.run/functions/v1/mcp-run-v1

    Troubleshooting

    SymptomCauseFix
    401 UnauthorizedMissing or invalid API keyCheck your ak_ key is valid
    403 ForbiddenInsufficient scopeEnsure your key has required scopes
    406 not_acceptableAccept header missing application/json or text/event-streamSend both: 'Accept: application/json, text/event-stream'
    429 Too Many RequestsRate limit exceededWait and retry
    Body starts with "data: {…}"SSE framing on every response (single-shot too)Strip 'data:' prefix; read result.structuredContent for parsed output
    "Resource not found" (ChatGPT)Bridge wrapper expiredRe-trigger tool discovery
    Different instance_id each callNormal serverless behaviorExpected, server is stateless

    See Also