Jobs Navi API for AI Agents

An MCP server that cross-searches physicalai-jobs.com × humanoid-jobs.com and also lets you edit your own jobs and profile.
24 read/write + 12 automation, analytics & AI-assist — 37 tools total, callable from Claude Code / Cursor / HTTP API.

37 Tools Read Write Webhooks AI Helpers Production Security 2-Site Cross-search

Why MCP?

The answer to "AI can read the site directly, so why bother with MCP?" For a one-off question it is indeed unnecessary — but for cross-site search, scheduled runs, strict filtering and bulk retrieval it makes a real difference.

Aspect Web access (no MCP) Via MCP
Data format LLM parses HTML Structured JSON returned instantly
Response speed Several to tens of seconds ~100ms (direct DB)
2-site cross-search Crawl humanoid and physicalai separately Both in one request (omit site_code)
Filter precision Only what the UI can show Strict salary_min / is_remote / prefecture etc.
Bulk retrieval Scraping risks being blocked All records safely via offset/limit
Resilience to site changes Breaks on layout changes Stable with a fixed schema
Scheduled runs (cron) Launch a browser every time → slow & heavy Done with a single curl

In short: if you "just want to ask AI one thing right now" it is unnecessary. If you want to build it into an automated workflow, aggregate daily, or search both sites accurately at once, MCP is overwhelmingly easier.

Use Cases

Representative scenarios where an AI agent uses job data across physicalai-jobs.com / physicalai-jobs.com.

Cross-site job search

Scenario: "Find remote jobs in humanoid or physical AI paying ¥8M+."
Action: Just call jobs_search without specifying a site to search both at once.

jobs_search jobs_detail categories

Company research

Scenario: "List all companies like Boston Dynamics with their tech stack and job count."
Action: companies_search → companies_detail to get tech stack and open roles.

companies_search companies_detail

Industry news analysis

Scenario: "Summarize the latest trends in the humanoid industry from articles."
Action: articles_search to fetch articles → articles_detail to analyze the body.

articles_search articles_detail

Market monitoring

Scenario: "Report the weekly trend of humanoid vs physical-AI job counts."
Action: Just hit stats via a weekly cron and post to Slack.

stats sites_list health

Quick Start

Connect to Jobs Navi in 3 steps from MCP-capable AI such as Claude Code / Cursor.

  1. ① (Write only) Issue an API key
    Log in to physicalai-jobs.com → /mypage/api-keys/ to generate a key. Skip if you only read.
  2. ② Add the MCP server to your config file
    Add to Claude Code (.mcp.json) or Cursor (~/.cursor/mcp.json). env only for those who issued a key.
    { "mcpServers": { "jobs-navi": { "command": "npx", "args": ["-y", "jobs-navi-mcp"], "env": { "JOBS_NAVI_API_KEY": "jn_your_api_key_here" } } } }
  3. ③ Restart the AI client
    On startup it auto-fetches jobs-navi-mcp via npx. After restart, 37 tools are recognized.
  4. ④ Ask in natural language
    # Read example > Any robotics engineer jobs in Tokyo paying ¥8M+?> List all jobs Boston Dynamics is posting now # Write example (API key required) > Create a new job: Senior Robotics Engineer, ¥9M–15M, remote OK> Update our company profile description to "Series B closed in FY2026"> Change my desired salary to ¥9M and set Tokyo/Kanagawa as preferred

Connection Methods

Connect three ways: Claude Code / Cursor / HTTP API. With HTTP API you can also call it from ChatGPT Custom GPT Actions.

Add the following to .mcp.json or ~/.claude.json. JOBS_NAVI_API_KEY is required for write tools (optional for read-only).

{ "jobs-navi": { "command": "npx", "args": ["-y", "jobs-navi-mcp"], "env": { "JOBS_NAVI_API_KEY": "jn_your_api_key_here" } } }

Add the following to ~/.cursor/mcp.json.

{ "mcpServers": { "jobs-navi": { "command": "npx", "args": ["-y", "jobs-navi-mcp"], "env": { "JOBS_NAVI_API_KEY": "jn_your_api_key_here" } } } }

* ChatGPT does not support Remote MCP. Call the HTTP API from Custom GPT Actions.

# Custom GPT Actions setup # Server URL https://physicalai-jobs.com/api/mcp.php # Method: POST / Body example { "action": "jobs.search", "params": { "keyword": "robotics" } }

Reads need no auth; writes require Authorization: Bearer jn_xxx.

# Read (no auth) curl -X POST https://physicalai-jobs.com/api/mcp.php \ -H "Content-Type: application/json" \ -d '{"action": "jobs.search", "params": {"keyword": "robot", "limit": 5}}'
# Write (API key required) curl -X POST https://physicalai-jobs.com/api/mcp.php \ -H "Authorization: Bearer jn_your_api_key_here" \ -H "Content-Type: application/json" \ -d '{"action": "jobs.publish", "params": {"id": 15}}'

Authentication

The 12 read tools need no auth. The 12 write tools (create/update/publish jobs, edit company/seeker profile, etc.) require an API key in jn_xxx format.

① Issue an API key

After logging in to physicalai-jobs.com, /mypage/api-keys/ generate a key. jn_ + 48 hex chars = 51 chars, up to 5 keys.

② Send as a Bearer Token

For direct HTTP calls, put it in the Authorization header. The Node.js MCP attaches it automatically via the JOBS_NAVI_API_KEY env var.

Authorization: Bearer jn_a1b2c3d4e5f6...

③ You can edit only your own resources

Since the API key is tied to the issuing user, you can update only your own jobs / your own profile. Other companies' jobs are read-only.

Key spec + security

For the detailed spec, defense-in-depth and rate limits, see the Security / Rate Limits sections.

  • SHA-256 hashed storage (the plaintext key cannot be recovered even if the DB leaks)
  • Only your own resources are operable (others = 404)
  • Rate limits Read 200/min, Write 30/min
  • Instant revocation /mypage/api-keys/ via the "Revoke" button

API Reference - 37 Tools

All 37 tools. Click to expand parameters and a response example. Omit site_code for cross-site search. Tools marked [API key] are for writes / operating your own resources.

Utility

3 tools

MCP server health check. Returns job/company/article counts per site.

No parameters required.

Response Example
{ "success": true, "data": { "status": "ok", "sites": [ { "site_code": "humanoid", "name": "ヒューマノイドジョブ", "domain": "humanoid-jobs.com", "job_count": 1, "company_count": 1, "article_count": 0 }, { "site_code": "physicalai", "name": "フィジカルAIジョブ", "domain": "physicalai-jobs.com", "job_count": 0, "company_count": 0, "article_count": 0 } ] } }

Returns the list of cross-searched sites (code, name, domain, theme color).

No parameters required.

Response Example
{ "success": true, "data": [ { "site_code": "humanoid", "name": "ヒューマノイドジョブ", "domain": "humanoid-jobs.com", "specialty": "ロボット・ヒューマノイド人材特化", "theme_color": "#0EA5E9" }, { "site_code": "physicalai", "name": "フィジカルAIジョブ", "domain": "physicalai-jobs.com", "specialty": "フィジカルAI人材特化", "theme_color": "#0d9488" } ] }

Returns the job category list with job counts.

ParameterTypeRequiredDescription
site_code string humanoid / physicalai (omit for cross-site)
Response Example
{ "success": true, "data": [ { "site_code": "humanoid", "slug": "robot-engineer", "name": "ロボットエンジニア", "parent_slug": null, "description": null, "job_count": 1 } ] }

Jobs

3 tools

Get full job details (skill requirements, benefits, company info).

ParameterTypeRequiredDescription
id number Job ID (id or slug required)
slug string Job slug
site_code string Target site when using slug
Response Example
{ "success": true, "data": { "id": 1, "title": "ヒューマノイドロボットエンジニア", "description": "...", "skills_required": [ "ROS2", "C++", "Python" ], "company_name": "Sample Robotics", "industry": "ロボティクス" } }

List of new or featured jobs (for top page / widgets).

ParameterTypeRequiredDescription
site_code string humanoid / physicalai
featured_only boolean true = featured jobs only
limit number Result count
Response Example
{ "success": true, "data": [ { "id": 1, "title": "求人例", "is_featured": 1, "company_name": "Sample Robotics" } ] }

Companies

2 tools

Company details (tech stack, office images, active job list).

ParameterTypeRequiredDescription
id number Company ID (id or slug required)
slug string Company public slug
site_code string Target site when using slug
Response Example
{ "success": true, "data": { "id": 1, "company_name": "Sample Robotics", "tech_stack": [ "ROS2", "Python" ], "active_jobs": [ { "id": 1, "title": "エンジニア" } ] } }

Articles

2 tools

Get article details including the full body.

ParameterTypeRequiredDescription
slug string YES Article slug
site_code string Target site
Response Example
{ "success": true, "data": { "slug": "example", "title": "記事タイトル", "body": "記事本文..." } }

Stats & Plans

2 tools

Per-site statistics (active jobs, companies, seekers, applications, articles).

ParameterTypeRequiredDescription
site_code string humanoid / physicalai
Response Example
{ "success": true, "data": [ { "site_code": "humanoid", "active_jobs": 1, "companies": 1, "seekers": 0, "applications": 0, "articles": 0 } ] }

Company posting plan list (plan name, monthly price, job slots, scout slots, features).

ParameterTypeRequiredDescription
site_code string humanoid / physicalai
Response Example
{ "success": true, "data": [ { "slug": "basic", "name": "ベーシック", "price_monthly": 30000, "job_limit": 3 } ] }

Auth

1 tools

[API key] Returns the user tied to the API key. Use to verify the connection.

No parameters required.

Response Example
{ "success": true, "data": { "user_id": 2, "email": "company@example.com", "name": "テスト株式会社", "role": "company" } }

Jobs Write

5 tools

[API key / Company] Returns your company's posted jobs (including drafts).

ParameterTypeRequiredDescription
status string draft / active / closed / paused
limit number Result count (default 30, max 100)
Response Example
{ "success": true, "data": [ { "id": 1, "title": "求人", "status": "active", "view_count": 2 } ] }

[API key / Company] Create a new job (saved as draft). Publish with jobs_publish.

ParameterTypeRequiredDescription
title string YES Job title
category_slug string YES Category slug
description string YES Job description body
employment_type string full_time / part_time / contract / internship / remote
location string Work location
is_remote boolean Remote OK
salary_min number Minimum annual salary (JPY)
salary_max number Maximum annual salary (JPY)
requirements string Required qualifications
preferred string Preferred qualifications
benefits string Benefits
Response Example
{ "success": true, "message": "求人を下書きとして作成しました", "data": { "id": 15, "slug": "job-abc123", "status": "draft" } }

[API key / Company] Update your job (only specified fields are overwritten).

ParameterTypeRequiredDescription
id number YES Job ID to update
title string Title
salary_min number Minimum annual salary
salary_max number Maximum annual salary
description string Body
Response Example
{ "success": true, "message": "求人を更新しました", "data": { "id": 15 } }

[API key / Company] Publish a job (draft → active).

ParameterTypeRequiredDescription
id number YES Job ID to publish
Response Example
{ "success": true, "message": "求人を公開しました", "data": { "id": 15, "status": "active" } }

[API key / Company] Close a job (active → closed).

ParameterTypeRequiredDescription
id number YES Job ID to close
Response Example
{ "success": true, "message": "求人を終了しました", "data": { "id": 15, "status": "closed" } }

Company Profile

2 tools

[API key / Company] Get your company profile.

No parameters required.

Response Example
{ "success": true, "data": { "id": 1, "company_name": "テスト株式会社", "industry": "ロボティクス", "employee_count": "50-100", "website_url": "https://example.com" } }

[API key / Company] Update your company profile (created if not present).

ParameterTypeRequiredDescription
company_name string Company name (required on first creation)
company_name_kana string Furigana (kana)
industry string Industry
employee_count string Employee count (e.g. 50-100)
founded_year number Year founded
website_url string Official site URL
logo_url string Logo URL
description string Company description
address string Address
phone string Phone number
contact_person string Contact person name
Response Example
{ "success": true, "message": "企業プロフィールを更新しました", "data": { "id": 1 } }

Seeker Profile

2 tools

[API key / Job seeker] Get your job-seeker profile.

No parameters required.

Response Example
{ "success": true, "data": { "id": 1, "full_name": "テスト 太郎", "prefecture": "東京都", "experience_years": 5, "desired_job_category": "robot-engineer", "is_public": 1 } }

[API key / Job seeker] Update your profile (created if not present).

ParameterTypeRequiredDescription
full_name string Full name (required on first creation)
full_name_kana string Furigana (kana)
birth_year number Birth year (AD)
gender string male / female / other / prefer_not
prefecture string Prefecture of residence
experience_years number Years of experience
current_status string employed / unemployed / student / other
desired_job_category string Desired job category
desired_salary_min number Desired minimum salary (in 10k JPY)
resume_url string Resume URL
pr_text string Self PR
is_public boolean Whether to expose to companies
Response Example
{ "success": true, "message": "求職者プロフィールを更新しました", "data": { "id": 1 } }

Applications

2 tools

[API key] Application list. Company role: applications to your jobs; seeker role: applications you submitted.

ParameterTypeRequiredDescription
limit number Result count (default 30, max 100)
Response Example
{ "success": true, "data": [ { "id": 1, "job_id": 1, "job_title": "エンジニア", "applicant_name": "テスト太郎", "status": "pending", "created_at": "2026-05-01 10:00:00" } ] }

[API key / Company] Update the status of an application to your job.

ParameterTypeRequiredDescription
id number YES Application ID
status string YES pending / reviewing / accepted / rejected / withdrawn
Response Example
{ "success": true, "message": "応募ステータスを更新しました", "data": { "id": 1, "status": "accepted" } }

Webhooks

3 tools

[API key] Register a webhook that POSTs on events (max 5).

ParameterTypeRequiredDescription
url string YES POST target URL (https:// required)
events array YES new_application / application_status_changed / new_scout / job_published / job_closed
label string Label for identification
Response Example
{ "success": true, "message": "Webhookを登録しました", "data": { "id": 1, "url": "https://example.com/webhook", "events": [ "new_application" ], "secret": "abc123..." } }

[API key] List of registered webhooks (with call / failure counts).

No parameters required.

Response Example
{ "success": true, "data": [ { "id": 1, "url": "https://example.com/webhook", "events": [ "new_application" ], "total_calls": 12, "total_failures": 0 } ] }

[API key] Delete a webhook.

ParameterTypeRequiredDescription
id number YES Webhook ID
Response Example
{ "success": true, "message": "Webhookを削除しました", "data": { "id": 1 } }

Notifications

2 tools

[API key] Returns your notification list and unread count.

ParameterTypeRequiredDescription
unread_only boolean true = unread only
limit number Result count (default 30)
Response Example
{ "success": true, "data": { "unread_count": 3, "items": [ { "id": 1, "type": "new_application", "title": "新着応募がありました", "is_read": 0 } ] } }

[API key] Mark notifications as read (ID array or "all").

ParameterTypeRequiredDescription
ids mixed YES ID array [1,2,3] or "all"
Response Example
{ "success": true, "message": "全ての通知を既読にしました", "data": { "marked": "all" } }

Bulk & Analytics

4 tools

[API key / Company] Bulk-create jobs (max 50, each as draft).

ParameterTypeRequiredDescription
jobs array YES Array of job objects. Each element uses the same fields as jobs_create
Response Example
{ "success": true, "message": "全ての求人を作成しました", "data": { "created_count": 50, "error_count": 0, "created": [ { "index": 0, "id": 15, "slug": "...", "title": "..." } ], "errors": [] } }

[API key / Company] Clone an existing job (as draft).

ParameterTypeRequiredDescription
id number YES Source job ID
title string Title of the new job
Response Example
{ "success": true, "message": "求人を複製しました", "data": { "id": 20, "slug": "...", "cloned_from": 15, "status": "draft" } }

[API key / Company] Per-job views, applications, favorites and CVR.

ParameterTypeRequiredDescription
id number Job ID (omit to aggregate all jobs)
Response Example
{ "success": true, "data": { "total_jobs": 5, "total_views": 1234, "total_applications": 42, "overall_conversion": 3.41, "jobs": [ { "id": 1, "title": "...", "view_count": 500, "application_count": 15, "favorite_count": 23 } ] } }

[API key / Company] Returns applicant info in a flat structure (for CSV conversion).

ParameterTypeRequiredDescription
job_id number Filter by a specific job
status string pending / reviewing / accepted / rejected / withdrawn
Response Example
{ "success": true, "data": { "count": 1, "applicants": [ { "id": 1, "job_title": "ROS2エンジニア", "applicant_email": "...", "full_name": "テスト 太郎", "prefecture": "東京都", "status": "pending" } ] } }

AI Helpers

3 tools

[API key] Computes salary statistics (median, quartiles) from active jobs on the site.

ParameterTypeRequiredDescription
category_slug string Category slug
prefecture string Prefecture
is_remote boolean Remote OK only
Response Example
{ "success": true, "data": { "sample_size": 42, "min": 3000000, "max": 15000000, "median": 7500000, "p25": 5500000, "p75": 10000000, "avg": 7800000, "unit": "円 / 年" } }

[API key] Returns jobs similar to the given job (scored).

ParameterTypeRequiredDescription
id number YES Reference job ID
limit number Result count (default 10, max 30)
Response Example
{ "success": true, "data": { "source_id": 15, "results": [ { "id": 18, "title": "類似求人", "similarity_score": 85, "company_name": "Sample" } ] } }

[API key / Company] Scores your job on 10 criteria. Returns grade A-D and improvement hints.

ParameterTypeRequiredDescription
id number YES Job ID to score
Response Example
{ "success": true, "data": { "job_id": 15, "score": 72, "grade": "B", "checks": [ { "name": "タイトル20文字以上", "pass": true, "weight": 10 } ], "summary": "良好。改善するともっと応募が増えます" } }

Market Signals (Investor)

1 tools

Returns a per-company hiring-momentum time series (active/new job counts over time, surge flags) with securities (ticker) codes; listed companies linked to ticker/exchange/country.

ParameterTypeRequiredDescription
site_code string humanoid / physicalai / prompters (omit for cross-site)
ticker string Filter by securities code (e.g. TSLA, 7203)
company string Company name keyword (partial match)
period string weekly (default) / monthly
group_by string company (default) / ticker = aggregate by security (merges name variants & sources; members[] lists the components)
limit number Recent periods to return (default 26, max 104)
public_only boolean true = listed companies (ticker-linked) only
min_delta_pct number Surge threshold, period-over-period % (default 50)
Response Example
{ "success": true, "data": { "as_of": "2026-05-19", "period": "weekly", "count": 12, "top_surging": [ { "company": "Tesla, Inc.", "ticker": "TSLA", "exchange": "NASDAQ", "country": "US", "active_delta_pct": 120, "latest_active": 11, "latest_new": 6 } ], "series": [ { "company": "Tesla, Inc.", "ticker": "TSLA", "exchange": "NASDAQ", "country": "US", "is_public": true, "relation": "pure_play", "site_code": "humanoid", "latest_active": 11, "prev_active": 5, "active_delta_pct": 120, "latest_new": 6, "surge": true, "points": [ { "period_end": "2026-05-12", "active_jobs": 5, "new_jobs": 2 }, { "period_end": "2026-05-19", "active_jobs": 11, "new_jobs": 6 } ] } ] } }

Rate Limits

To prevent DoS and abuse, rate limiting is implemented in two tiers: Nginx (L4) + PHP (L7). On exceed, returns HTTP 429 + Retry-After header.

Layer 1: Nginx (per IP, burst supported)

EndpointLimitBurstReason
/api/auth.php1 req/sec5Brute-force protection
/api/mcp.php5 req/sec20DoS protection (per IP)

Layer 2: PHP (per API key / IP, 1-minute bucket)

CategoryLimitIdentifier
Unauthenticated Read60 req/minIP address
Authenticated Read200 req/minAPI key ID
Write30 req/minAPI key ID

Response example (on exceed)

# HTTP/2 429 # Retry-After: 32 { "success": false, "message": "Rate limit exceeded: write is limited to 30 times per minute", "retry_after_sec": 32 }

Clients should wait the Retry-After header seconds before retrying.

Security

Security measures required for production are implemented as defense-in-depth.

🛡️ Defense-in-depth architecture

LayerMeasureDetail
L4 (Nginx)IP-based rate limitingDrops excess requests before reaching PHP
HTTP HeadersSecurity headersHSTS / X-Content-Type-Options / Referrer-Policy / Permissions-Policy / CSP frame-ancestors
CORSOrigin whitelistAllow only physicalai-jobs.com / physicalai-jobs.com / kyuujin.prompters.jp / asi.co.jp
L7 authAPI key verificationBearer + regex match + SHA-256 hash compare (timing-attack resistant)
L7 rate limitPer API key / IPSeparate Read/Write caps; 429 + Retry-After on exceed
Role checkcompany / seeker boundaryRole violation = 403, accessing others' resources = 404
SQL InjectionPDO Prepared StatementAll queries use placeholders; dynamic parts like LIMIT are intval-cast
SSRF protectionWebhook URL validationlocalhost / 10.x / 192.168.x / 172.16-31.x / 169.254.x / link-local denied
Brute-forceLogin attempt trackingLock 15 min after 5 email / 20 IP failures
SessionCookie hardeningHttpOnly / Secure / SameSite=Lax
Error infoNot exposedException details only in error_log; clients get a generic message

🔑 API key spec

ItemValue
Formatjn_ + hex 48 chars = 51 chars
Entropy192-bit (24-byte random)
StorageSHA-256 hash (no plaintext)
ScopeOnly the issuing user's resources
Max keys5 per user
RevocationAvailable instantly (/mypage/api-keys/)

⚠️ If a key may have leaked, revoke it immediately. After revocation the next request returns 401.

📊 Monitoring dashboard

Administrators can monitor in real time on this dashboard: /admin/security/

  • Login failure / success summary for the last 24h
  • Failures per IP (≥5 = watch, ≥20 = locked)
  • Failures per account + distinct IP count (password-spray detection)
  • Timeline of the latest 50 failures (incl. User-Agent)
  • Issued API key list (call count, last used)

Logs retained 30 days (auto-cleanup via daily cron).

🚨 Vulnerability reporting

If you find a security vulnerability, please contact security@physicalai-jobs.com . We will provide a patch before disclosure.

Error Handling

On error, returns success: false with an HTTP status code. Exception details are not exposed to clients (generic message only).

{ "success": false, "message": "slug or id is required" }
HTTPMeaningExample cause
400Bad RequestMissing required params / invalid value / invalid JSON / SSRF-target URL
401UnauthorizedAPI key invalid / expired / malformed
403ForbiddenRole violation (e.g. a seeker calling a company-only tool)
404Not FoundResource not found or no edit permission
405Method Not AllowedAccessed via GET (POST only)
429Too Many RequestsRate limit exceeded / brute-force lock (see Retry-After)
500Server ErrorInternal error (details only in error_log)

Build your career in
Physical AI

Job listings from leading Physical AI companies (humanoid, industrial robots, AMRs) including Tesla Optimus, Figure 02 and Unitree.
Roles span Physical AI engineers, consultants, AI researchers, operators and more.